How to use state pattern correctly?

24.9k views Asked by At

I've encountered a few implementations of state pattern in my programming experience, and done a few. I've seen them used in various scenarios (mostly UI and parsing). The trouble is that all of them under pressure of rapid development turned into hardly maintainable and understandable chunks of code. I'm considering refactoring one of these, but I'm having trouble finding good resources for this online. There are many simple examples of State Pattern online, but I need some more in depth resources.

So I'm looking for:

  • Examples of common pitfalls when implementing state pattern and how to avoid them,
  • Real world examples of state pattern done correctly (like in some open source project/framework)
  • Personal experiences with state pattern are also welcome

Thank you for your time

8

There are 8 answers

1
oosterwal On BEST ANSWER

@Ivan: There are a number of resources available on the web for Hierarchical State Machines (HSM). Miro Samek has written extensively about this design pattern and offers a lot of helpful information.

Some articles that should be of interest:

The big benefit of using HSM over flat FSM state charts described by Mealy and Moore is that the hierarchy creates a separation of responsibility. Sub-states only need to handle those conditions that they are expressly designed to handle--unhandled events get passed up to the parent state, if the parent state isn't expressly designed to handle it then it is passed up to the next-higher parent and so on. It allows you to create small, manageable state machines that each serve a single purpose--one that can fit within a single object. As new features are added, or as new classes are added, they only need to handle their own little part of the world and pass on the unhandled events to their respective parents.

When implemented correctly, you get a robust program with low cyclomatic complexity, that is easy to modify or upgrade as needed.

1
neuro On

Just my 2 cents, the state pattern always turns to be hard to maintain as it is hard to understand by those who has not coded it. I usually fallback to old standard array of function/method pointers, as I've in my old C experience. You just build a two dimensions array of function pointers with state/signal for lines/columns. Easier to understand. you have a class that manage that and you delegate to other class to handle complexity ...

my2c

0
Andriy Tylychko On

have a look at Finite State Machine. Almost every mature language has own good examples. Since you haven't specified your preferred language, I'll give you C++ example: Boost FSM library. Most probably it's much more complicated than you need, but it can give you some design hints for sure

0
orangepips On

As you probably have read, the State Design Pattern is useful when state varies the behavior of some object whose composition includes that state. This implies the idea of a State abstract class, interface, or enumerated type - albeit depending on the language Duck Typing will do as well - that defines any common behavior and/or required methods.

Key Aspects

There are really two key aspects to consider in working with the state pattern: enumeration and transition. Enumeration simply means identifying the set of possible states (e.g. days of the week), or more abstractly the types of states (i.e. meta states) such as starting, ending, and in between for a workflow engine.Transition means deciding how to model movement between states where this is typically either done by capturing all possible transitions in a tabular representation (i.e. Finite State Machine) or make each state know its possible "transitions" to other states.

Typically transitions go hand in hand with meta states because its not possible to know all states and relationships ahead of time in such a dynamic system where new states, and thus transitions, can be added at runtime. In addition, with the transition approach, certain behavior - notifications for instance - becomes part of the transition, instead of the state itself.

Examples

There are several scenarios I've either worked on or discussed where this is a use facility:

  1. Workflow
  2. Computer Game Opponent A.I.
  3. Process Orchestration

By workflow I mean something like jBPM. Systems like this are concerned with commanding the right attention, of the right people, at the right time. They usually send lots of emails or some other sort of notification. And, the process they represent needs the ability to change as the organization changes, whereas the data being managed typically changes much more slowly.

Computer Game Opponent A.I. is self explanatory. Not something I've written, but in conversation with those who have, these systems are usually self contained. In other words, unlike workflow, the game usually doesn't have the facility to alter the process used to control computer opponents.

Process Orchestration is similar to workflow, but focused on system integration, instead of people interaction. The Apache Mule framework is an example. Here state can describe status (e.g. started, in process, ended) and type (e.g. ftp integration point, sql integration point).

Conclusion

Unlike other answers, I think state encapsulation is an excellent way to manage change in software systems. Done well, it facilitates those changes or enables users to do so at runtime. You make a tradeoff of more flexibility in exchange for increased implementation complexity. So such an approach is probably not useful for shopping cart for instance, where the behavior is probably very well known and not like to change. On the other hand when the process is subject to change, it makes a very good fit.

0
mbx On

You should use the state pattern, if you have a different behaviour for each state. Maybe you need to reconfigure the transitions on runtime. Another reason for using it is you might have to add more states later on.

Imagine a boardgame like Chinese Checkers you have different GUI States for picking a Pawn, select a target slot and so on. In each state the GUI should behave differently, some inputs should be handled others ignored. Using a simple switch/case is possible but state pattern comes handy as the logic is encapsulated, related code is toghether. This makes it easier to introduce new states without affecting most/all of the other states (depending on who is responsible to set the transitions: either the state knows its outgoing transitions, or they could be given at runtime e.g. using the constructor).

As you can see in this example, the GuiController uses an IGuiState Interface to change its behaviour on demand. An implementation can be seen here.

The main pitfall is to use switch/case, when you need to be flexible. Since the indirection takes a bit more time, I would recommend that for a fixed amount of rather simple staates. I you have to implement a rather fast low level network protocol, that is usually to much overhead.

1
Glenner003 On

Most of the times, the states in a state pattern design are handling more that one state (or substates of the state) which makes them harder to maintain.

If a state has any kind of selection in there, its mostly handling more than one state.

I takes a lot of discipline to keep the states clean.

A possible solution to this is to make more complex states statemachines themselves (HSM). This makes it a lot more readable at the upper level because it has to deal with fewer states.

0
user5484291 On

I am building a expression evaluator which has the ability to evaluate sets of elements. I found the state pattern very useful for discriminating what can and can't be done to a set depending on its state. ie: open,closed, inactive, active ect. FSM are very easy to draw and reduce the complexity of code by removing the need for huge blocks of ifelse statements to define what the feature should do depending on its enclosed attributes. It makes these conditions more explicit by making the conditions into classes. It's one of my favourite patterns so far.

1
Fuhrmanator On

So I'm looking for:

  • Examples of common pitfalls when implementing state pattern and how to avoid them,

The state pattern does not scale well. Just imagine a state machine with 10 states and 10 different transition types. Adding a new state means that state has to define all 10 transitions. Adding a new transition means all 10 states have to define it. In short, don't use the state pattern if your state machine isn't stable and/or you have a lot of states/transitions.

  • Real world examples of state pattern done correctly (like in some open source project/framework)

Define correctly :-) The Java example cited in https://stackoverflow.com/a/2707195/1168342 is for JSF Lifecycle, but I think there is only one transition. None of the other answers cite anything for State.

  • Personal experiences with state pattern are also welcome

Head First Design Patterns uses a Gumball machine example to illustrate State. It's ironic, but every time they extend the design (adding a new state or transition), there is a lot of repeated code (especially for the invalid transitions within specific states). Also, according to who decides what the next state is, individual state classes can be coupled to each other (inter-state dependencies). See the explanation at the end of this answer: https://stackoverflow.com/a/30424503/1168342.

The GoF book mentions that table-based alternatives have advantages, namely their regularity. Changing the transition criteria requires changing the table (and not the code).