Design Patterns
When I say Design Patterns here, the first thoughts of many will go to the Design Patterns book [GoF Design Patterns], which has been mentioned a whole bunch of times by now. It’s by no means the only book about Design Patterns, but it’s considered the standard work on the subject.
Design Patterns are abstract and pretty low-level in that they are technical and general in regard to domain. It doesn’t matter what tier or what type of system you are building, Design Patterns are still useful.
One way to describe Design Patterns is that they are about refining the subsystems or components. You will find when we move to the other two categories I’m about to discuss here that it’s common that the patterns there use one or more Design Patterns or that some specific Design Patterns can be applied in a more specific way in those categories.
The Design Patterns book [GoF Design Patterns] is pretty hard-going. Each time I read it I understand and learn more about it. For example, I have often thought, "That’s not correct" or "That’s not the best solution" or even "That’s stupid." But after some deliberation, I decide that they are "right" each time.
So far there has been a lot of talk and little action. It’s time to become concrete by giving an explanation of a Design Pattern. I have chosen one of my favorite Design Patterns called State, so here goes.
An Example: State Pattern
Problem-based teaching is a good pedagogic approach, so I’ll use it here. The following is a problem.
Problem
A sales order can be in different states, such as "NewOrder," "Registered," "Granted," "Shipped," "Invoiced," and "Cancelled." There are strict rules concerning to which states the order can "go" from which states. For example, it’s not allowed to go directly from Registered to Shipped.
There are also differences in behavior depending upon the states. For example, when Cancelled, you can’t call AddOrderLine() for adding more items to the order. (That also goes for Shipped and Invoiced, by the way.)
One more thing to remember is that certain behavior will lead to state transformation. For example, when AddOrderLine() is called, the state transforms from Granted back to New Order.
Solution Proposal One
In order to solve this problem, I need to describe a state graph in code. In Figure 2-1 you find a very simple and classic state graph describing how state of the button is changed between Up and Down each time the user pushes the button.
Figure 2-1 State graph for a button
If we apply this technique of a state graph on the Order, it could look like Figure 2-2.
Figure 2-2 State graph for an Order
One obvious solution is probably to use an enum like this:
public enum OrderState { NewOrder, Registered, Granted, Shipped, Invoiced, Cancelled }
and then to use a private field for the current state in the Order class, like this:
private OrderState _currentState = OrderState.NewOrder;
Then, in the methods, you need to deal with two things on top of what the methods should do. You must check if the method might be called at all in that state, and you need to consider if a transition should take place and, if so, to what new state. It could look like this in the AddOrderLine() method:
private void AddOrderLine(OrderLine orderLine) { if (_currentState == OrderState.Registered || _currentState == OrderState.Granted) _currentState = OrderState.NewOrder; else if (_currentState == OrderState.NewOrder) //Don't do any transition. else throw new ApplicationException(... //Do the interesting stuff... }
As you saw in the code snippet, the method got quite a lot of uninteresting code added just because of taking care of the state graph. An ugly if-statement is very fragile to changes in the future. Code similar to that will be sprinkled everywhere in the Order class. What we do is spread knowledge of the state graph in several different methods. This is a good example of subtle but evil duplication.
Even for simple examples like this, we should reduce the code duplication and fragmentation. Let's give it a try.
Solution Proposal Two
Proposal Two is just a slight variation. You can have a private method called _ChangeState(), which could, for example, be called from AddOrderLine(). _ChangeState() could have a long switch statement, like this:
private void _ChangeState(OrderState newState) { if (newState == _currentState) return; //Assume a transition to itself is not an error. switch (_currentState) { case OrderState.NewOrder: switch (newState) { case OrderState.Registered: case OrderState.Cancelled: _currentState = newState; Break; default: throw new ApplicationException(... break; } case OrderState.Registered: switch (newState) { case OrderState.NewOrder: case OrderState.Granted: case OrderState.Cancelled: _currentState = newState; break; default: throw new ApplicationException(... break; } ... //And so on... }
The AddOrderLine() now looks like this:
public void AddOrderLine(OrderLine orderLine) { _changeState(OrderState.NewOrder); //Do the interesting stuff... }
I was quite lazy in the previous code and only showed the start of the structure of the huge switch statement, but I think it's still pretty obvious that this is a good example of smelly code, especially if you consider that this example was simplified and didn't discuss all aspects or all states that were really needednot even close.
OK, I’ve been there, done that in several projects. I can’t say I like that solution very much. It seems fine at first, but when the problem grows, the solution gets troublesome. Let’s try out another one.
Solution Proposal Three
The third solution is based on a table (some kind of configuration information) describing what should happen at certain stimuli. So instead of describing the state transformation in code as in proposals one and two, this time we describe the transformations in a table, Table 2-1.
Table 2-1 State Transitions
Current State |
Allowed New State |
NewOrder |
Registered |
NewOrder |
Cancelled |
Registered |
NewOrder |
Registered |
Granted |
Registered |
Cancelled |
... |
... |
Then your _ChangeState() method can just check if the new state that comes as a parameter is acceptable for when the current state is NewOrder, for example. For the current state NewOrder, only Registered and Cancelled are allowed as a new state.
You could also add another column as shown in Table 2-2.
Table 2-2_State Transitions, Revised
Current State |
Method |
New State |
NewOrder |
Register() |
Registered |
NewOrder |
Cancel() |
Cancelled |
Registered |
AddOrderLine() |
NewOrder |
Registered |
Grant() |
Granted |
Registered |
Cancel() |
Cancelled |
... |
|
... |
Now your _ChangeState() method shouldn’t take the new state as a parameter, but rather the method name instead. Then _ChangeState() decides what the new state should be by looking in the table.
This is clean and simple. A big advantage here is that it’s very easy to get an overview of the different possible state transformations. The main problem is probably that it’s hard to deal with custom behavior depending upon the current state and then to go to one state of several possible states when a method executes. Sure, it’s no harder than with proposal two, but it’s still not very good. You could register information in the table about what delegates (a delegate is like a strongly typed function pointer) should be executed at certain transformations, and you could probably extend that idea to solve the other problems as well, but I think there is a risk that it gets a bit messy during debugging, for example.
Do we have more ideas? Let’s apply some knowledge reuse and try out the Design Pattern called State.
Solution Proposal Four
The general structure of the State pattern is shown in Figure 2-3.
The idea is to encapsulate the different states as individual classes (see -ConcreteStateA and ConcreteStateB). Those concrete state classes inherit from an abstract State class. Context has a state instance as a field and calls Handle() of the state instance when Context gets a Request() call. Handle() has different implementations for the different state classes.
Figure 2-3 State pattern, general structure
That’s the general structure. Let’s see what this could look like if we apply it to the problem at hand. In Figure 2-4, you find a UML diagram for the specific example.
Figure 2-4 State pattern, specific example
In the specific example, the Order class is the Context from the general structure. Again, Order has a field of OrderState, although this time OrderState isn’t an enum, but a class. For the sake of refactoring, your old tests might expect an enum, and then you can keep that enum as well (perhaps as a property which implementation inspects what is the current instance in the state inheritance hierarchy) and thereby not make changes to the external interface.
A newly created Order gets a new state instance of a NewOrder at instantiation and sends itself to the constructor, like this:
internal OrderState _currentState = new NewOrder(this);
Note that the field is declared as internal. The reason for this is so that the state class can change the current state by itself, so Order delegates the state transformations totally to the different state classes. (I could also let OrderState be an inner class of Order to avoid the need for internal.)
This time, the Register() method on Order is extremely simple. It could look like this:
public void Register() { _currentState.Register(); }
The Register() method on NewOrder is also pretty simple. At least it can focus on its own state, and that makes the code clean and clear. It could look like this:
public void Register() { _parent._Register(); _parent._currentState = new Registered(_parent); }
Before changing the state, there was kind of a callback to the parent (_parent._Register()) telling it to do its thing before the state was changed. (Note that the "callback" went to the internal method _Register() and not the public Register().) This is just one example of an option, of course. Other examples would be to put the code in the OrderState base class or in the NewOrder class itself. It should go wherever it’s best located.
As you saw, if I want to do things before or after the state transformation, it’s simple and very well encapsulated. If I want to disallow a certain transformation in the NewOrder class, I just skip implementing that method and use the implementation of the base class OrderState for that method. The implementation of the base class throws an exception saying it was an illegal state transformation, if that’s the wanted behavior. Another typical default implementation is to do nothing.
More Comments
When using the State pattern, we were actually swapping a single field into a bunch of separate classes. That doesn’t sound like a very good idea at first, but what we then get is the nice effect of moving the behavior to where it belongs and good alignment to the Single Responsibility Principle (SRP).
There are drawbacks, of course, and a typical one is that the program can potentially be flooded with small classes when we use a solution such as State.
Which solution you prefer is indeed up for debate, but I think the State pattern is one that should be seriously considered here. You might find that it solves your problem with the least amount of duplicated code and with the responsibility partitioned out into encapsulated and cohesive units, the concrete state classes. But watch out—the State pattern is also very easy to overuse, as is every tool. Use it wisely!
That was an example of a Design Pattern, a generic one. We’ll come back to more Design Patterns of another family, but first a discussion about another category of patterns.