By: P. Dyson, B. Anderson
Published in: PLoPD3
Category: Behavioral, Finite State Machines
Summary: Refinement and extension of State [Gamma+95]
To get different behavior from an object depending on its current state, encapsulate the state of the object in a "state" object. Delegate all state-dependent behavior to this state object.
To decide whether a data member belongs in the owning class or in the state object class, if a data member is only required for a single state, place it in the corresponding state object class. If the data member is required for some but not all states, place it in a common superclass. If the data member is state-independent, place it in the owning class and pass it to the state object if necessary.
You have a lot of state objects. To reduce the number required, when a state object has no state members, it represents pure state--nothing but state-specific behavior. A pure state object can be shared among any number of objects, reducing the number of state objects required.
To prevent the owning class from having too many state-specific, state-dependent methods, expose the state object by defining a method in the owning class that returns a reference to it. Make statement-specific inquiries directly to the state object.
To get the state object to change when the owning object's state changes, have the state object initiate the transition from itself (the current state) to the new state object. This ensures that transitions are atomic and removes state-dependent code.
To reuse state object classes among owning classes with different state-transition profiles, if state object classes are used by more than one owning class, and those owning classes have different FSMs, have the owning class initiate the transition between states.
When creating a new owning object, to ensure it has the correct initial state object, use a method, called by the initialize method, that returns the default state object. Redefine this method in a subclass if a different default state is required.