An article about implementing state in a C# application, using the State Software Design Pattern.
Level: Intermediate to Advanced
About The State Software Design Pattern
The extract above describes the primary reason for applying the State Design Pattern - to retract overly-complex conditional logic. I have a secondary reason for using this pattern, which is....applications get scattered with code representing state values.
A conventional approach of applying a state process model to a software application is to create a lookup table in a database and add state values(ID's) and descriptions to said table. Application code would then use integer values to represent the state lookup values(ID's) of the table. Applications using these hard-coded integers is susceptible to typos and lots of editing and replacing when somebody decides to change the values during development of the model.
An alternative to scattering integers everywhere is to implement an enumerated type that models the states and then to use the enumerated types in place of the integers. This is an improvement but still presents additional maintenance in keeping the enumeration and the lookup table in sync.
Both of these techniques present problems and neither satisfy the primary and secondary problems I have just described. Let's take a look at an example and see how we can apply a more effective design using the State Software Design Pattern. I've created a mock-up; a fictional business problem we can apply this pattern to.
My Fictional Problem
Users are gathering content from third-parties and creating internal documents with it. The content usually has spelling mistakes and grammatical errors and requires some kind of validation and formal sign-off process.
Bring in the UML Diagrams
A UML State Model Diagram
Below is a simple document state process model I am going to use to solve my problem.
Note. This State Process Model would require more states if it was to be applied to a real-world solution. However, I am keeping this example simple for the purpose of this article.
Applying the State Design Pattern
I'm starting by presenting the kind of State Pattern UML Class Diagram you're already familiar with. I've no doubt you have seen this if you have read any Gang Of Four (GoF) publications, or anything by Martin Fowler about the State Design Pattern.
The UML Class Diagram
Looks good doesn't it? It looks "Designed"; Object-Oriented! We're on to a winner. However, I'm sure you've seen many class diagrams like this, understood the diagram at a abstract level, but then wondered "how the hell do I implement this?". Well, hopefully I can help you out here. I'm going to take these diagrams one step further and show you an implementation of how the context interacts with the State Pattern. Before I do this I would like to take a step backwards....
An Unconventional Approach: Dropping the State Lookup Table
I'm not going to create anything in a database to model our state process. I'm not creating a lookup table. My state process model is representing a solution to a business problem; it's a solution created for managing the quality of content going in to internal documents. This is business logic (also known as domain logic), so I don't want the database to be responsible for maintaining it.
Ok, so you've picked yourself up off the floor....let's continue, back to the class diagram and applying the pattern.
Creating the C# Classes for State Pattern
Here is the first pass implementation of the classes in the UML class diagram. Note. I have added a prefix of Base to the DocState and a suffix of State to the concrete states.
What we have now is a code skeleton that maps on to our class diagram. Time to flesh out the classes and show you how they interact.
The Context Class
I'm beginning with the context class, DocBll, because the design will be easier to digest starting with the problem domain.
The first step is to add a state variable to the context, which represents the current state of a document. At the same time I'll throw in a couple of variables to store the data of the document. The variables won't be used in my examples but it will give you a better feel for the class design.
Now I'll flesh out one of the public methods. I'm going to start with SubmitForReview method in the context class, and you're probably wondering why. Why don't I start with CreateDoc as it's the first method in our process? I will explain shortly. Here is a fleshed-out SubmitForReview method:
"Is this it? What is this going to achieve?", I hear you ask. Well, not a lot to be honest. Let's expand it a little....
What we really need is to be able to pass information about the context object, our DocBll, through to our current state pattern object. This wasn't included in our class diagram, but I wanted to keep the diagram as simple as I possibly could. I am going to introduce a new class here, and the sole purpose of this class is to provide a communication link between the context, and the current state.
Our class takes a context object in the constructor so it can expose only the fields that should be exposed to the state pattern, as opposed to every public member of the context object. Also, our class contains two properties which the state will fill in: The NextState, which is the state the context will be moving to (our transition), and HasSignature, which is a boolean indication of whether or not the context is in a valid state to proceed with the request that was made on it.
By design, I have decided that I don't want our state pattern to have direct database access - I don't want it to persist anything. It should simply validate and provide us with authorisation through the HasSignature property.
Here is the DocState Class with its new signature and the DocBll with an implementation of SubmitForReview.
Let's get back to the CreateDoc method....
By default, when a class is instanciated all its object members will be initialized to null, which means that when the DocBll context class is instanciated the state variable _currentState will be null, and will still be null CreateDoc is called. This means that the implementation of CreateDoc will have to do something extra. It will have to test for a null value and create a state if the test returns true. Here's an implementation of the CreateDoc method:
There's actually no reason why the CreateDoc method should be implemented differently to the other methods on DocBll, and with the addition of one small class consistency will be brought back in line - there will be no need for the null checks we can see in the previous code example.
The Null Object Software Design Pattern
Here is NullDocState Null Object Pattern implementation:
As well as providing you with a Null Object definition, here I have given you a full implementation of the SignOff method.
Here is a complete implementation of the DocBll Context Class
The Concrete State Classes
Here is the implementation of the modelled conrete states:
To Conclude Implementing the State Design Pattern in C#
I have given you a very simplistic solution, demonstrating the use of the State Design Pattern, and the transformation of a business problem through analysis, class design, through to the implementation. I hope my writing is clear and you feel you now have the understanding to apply these techniques in your own solutions.
Missing from this Article
What this article didn't cover is how to persist a context's state to a database and then load the context object with state. I will be following this article up with a part 2 where I will add a little more functionality to these classes and also show you how to persist and load.
Useful Links
- The State Pattern: An Underappreciated Pattern by Jeremy D. Miller
- State Pattern from dofactory.com