Collective Wisdom from the Experts 169
In my work, I see both missing checks and redundant checks all the time. This
example is tiny, but when you add cancellation and repayment, it’ll become
more complex, and the need for good state handling increases. In this case, an
order can only be in one of three distinct states:
- In progress: Can add or remove items. Can’t ship.
- Paid: Can’t add or remove items. Can be shipped.
- Shipped: Done. No more changes accepted.
These states are important, and you need to check that you’re in the expected
state before doing operations, and that you only move to a legal state from where
you are. In short, you have to protect your objects carefully, in the right places.
But how do you begin thinking in states? Extracting expressions to meaningful
methods is a very good start, but it is just a start. The foundation is to under-
stand state machines. I know you may have bad memories from CS class, but
leave them behind. State machines are not particularly hard. Visualize them to
make them simple to understand and easy to talk about. Test-drive your code
to unravel valid and invalid states and transitions and to keep them correct.
Study the State pattern. When you feel comfortable, read up on Design by
Contract. It helps you ensure a valid state by validating incoming data and the
object itself on entry and exit of each public method.
If your state is incorrect, there’s a bug, and you risk trashing data if you don’t
abort. If you find the state checks to be noise, learn how to use a tool, code
generation, weaving, or aspects to hide them. Regardless of which approach
you pick, thinking in states will make your code simpler and more robust.