State
The state pattern
The state pattern is a behavioral pattern and is quite an interesting pattern to prevent those huge branching if ... else or switch ... case statements, that I see ubiquitously in enterprise size solutions.Maybe, some wouldn't approve of the number of classes that would be needed but it's worth considering as it could make the solution, more easily scalable and ultimately refactorable (if you want that type of thing).
Below is the structure of the pattern and the abstraction State is used by the Context, which doesn't really do too much, apart from allowing a call to the State's Handle() method.
Now either the Context or the State can change state but, I think, it's a little cleaner to change state in the State classes otherwise you'd just need to have the branching code that I mentioned earlier.
On to an example, firstly the State interface:
interface State {
void Handle(Context context);
}
I've created a traffic light state machine and so here's the GreenState class, that implements State:
void Handle(Context context);
}
class GreenState : State {
public void Handle(Context context) {
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Green");
context.State = new AmberState();
}
}
The GreenState changes the state to the AmberState, which is:
public void Handle(Context context) {
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Green");
context.State = new AmberState();
}
}
class AmberState : State {
public void Handle(Context context) {
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Amber");
context.State = new RedState();
}
}
The next state is the RedState:
public void Handle(Context context) {
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Amber");
context.State = new RedState();
}
}
class RedState : State {
public void Handle(Context context) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Red");
context.State = new RedAmberState();
}
}
And the final state is RedAmberState, before returning to the GreenState:
public void Handle(Context context) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Red");
context.State = new RedAmberState();
}
}
class RedAmberState : State {
public void Handle(Context context) {
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("Red");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(" Amber");
context.State = new GreenState();
}
}
public void Handle(Context context) {
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("Red");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(" Amber");
context.State = new GreenState();
}
}
Next is the Context class and this is the engine behind the State Pattern, it's very simple but allows the states to be changed just by calling Request():
class Context {
public State State { get; set; }
public void Request() {
State.Handle(this);
}
}
public State State { get; set; }
public void Request() {
State.Handle(this);
}
}
A console application to test is:
static void Main(string[] args) {
Context context = new Context { State = new RedState() };
for(int i=0; i<9; i++) {
context.Request();
}
Console.ReadKey();
}
With the output being:
Context context = new Context { State = new RedState() };
for(int i=0; i<9; i++) {
context.Request();
}
Console.ReadKey();
}
Red
Red Amber
Green
Amber
Red
Red Amber
Green
Amber
Red
Summary
The state pattern is a simple but powerful pattern and handy if you would like to escape from large branching statements.
The cost of avoiding a large number of branching statements is the number of classes being high, but this should make you're solution
more scalable and easier to refactor.
One issue, in my code, is the new operator which could be handled with each state requiring the next state to be injected.
This proves to be very handy as the states can be arranged during runtime, which makes this pattern all the more powerful.