Design Patterns: State

Certainly you have seen code that changes the state of an entity, and that code is full of switch or if statements checking the current state of the entity in order to perform another operation that might or not change the state of the entity itself. In many enterprise applications that can be quite messy and often we have to change the behavior of it or chase a hard-to-spot bug (I have some horror stories, I bet you have as well).

That is common when you have Transaction Scripts implementing the business logic. Let’s consider this scenario where we have a publishing software with this workflow:

Diagram 1. Document States

We can implement this as a transaction script as follows:

public class Document
{
    public DocumentState State { get; set; }
}

public enum DocumentState
{
    Draft,
    Reviewing,
    Published,
    Retired
}

public class DocumentService
{
    public void Publish(Document doc)
    {
        if (doc.State == DocumentState.Draft)
        {
            // do other stuff
            doc.State = DocumentState.Reviewing;
        } 
        else if (doc.State == DocumentState.Reviewing)
        {
            // do other stuff
            doc.State = DocumentState.Published;
        } 
        else if (doc.State == DocumentState.Published)
        {
            throw new InvalidOperationException("Document is already published.");
        } 
        else if (doc.State == DocumentState.Retired)
        {
            // do other stuff
            doc.State = DocumentState.Draft;
        }
    }

    public void Reject(Document doc)
    {
        if (doc.State == DocumentState.Reviewing)
        {
            // do other stuff
            doc.State = DocumentState.Draft;
        } else
        {
            throw new InvalidOperationException("Document cannot be rejected in its present state.");
        }
    }

    public void Retire(Document doc)
    {
        if (doc.State == DocumentState.Published)
        {
            // do other stuff
            doc.State = DocumentState.Retired;
        } else
        {
            throw new InvalidOperationException("Document cannot be retired in its present state.");
        }

    }
}

As you can appreciate in the code above, we have a bunch of if statements checking for the current state of the entity in order to proceed or not with the changes. Of course, as in many examples and tutorials, the code is simplified in the sake of clarity. But that’s not too hard to see that this code can quickly become unmanageable. As business rules change, so the application code.

What would happen if we need to introduce an additional state? For now, it’s fairly easy. Just change a line of code here and there. But in the real world is fairly more complex as you need to include additional business logic. Compound that with the if statements and you will get a monster of a method with a growing chance of injecting a bug that will be hard to replicate or fix if not both.

To sum up, we will have maintainability hell down the road. Two SOLID principles are in violation here: DRY (Don’t Repeat Yourself) and Open-Closed. The first because we have the same state checking everywhere and also because it’s very likely that, with time, we’ll end-up copying chunks of code here and there to make ends meet. And the Open-Closed principle (which states that the class should be closed to modification but open to extension) is violated by forcibly needing to modify any of the DocumentService methods in order to accommodate logic for new states.

Okay! So what is this State Pattern design pattern anyways and how that will help me avoid the pitfalls of the previous code?

According to Wikipedia:

The state pattern is a behavioral software design pattern that allows an object to alter its behavior when its internal state changes. This pattern is close to the concept of finite-state machines. The state pattern can be interpreted as a strategy pattern, which is able to switch a strategy through invocations of methods defined in the pattern’s interface.

The state pattern is used in computer programming to encapsulate varying behavior for the same object, based on its internal state. This can be a cleaner way for an object to change its behavior at runtime without resorting to conditional statements and thus improve maintainability.

Wikipedia contributors. “State pattern.” Wikipedia, The Free Encyclopedia. Wikipedia, The Free Encyclopedia, 11 May. 2020. Web. 24 Jun. 2020.

So basically what that means is that we can encapsulate state-related or state-dependent behavior in state classes and using polymorphism change the state class instance (state transitions) as needed. So the business logic to transition to a new state will be implemented in each of these state classes thus avoiding the conditional statements checking for the entity state:

public abstract class DocumentState
{
    private Document document;

    public void SetDocument(Document document)
    {
        this.document = document;
    }

    public virtual void Publish()
    {
        throw new InvalidOperationException("Cannot publish document.");
    }

    public virtual void Reject()
    {
        throw new InvalidOperationException("Cannot publish document.");
    }

    public virtual void Retire()
    {
        throw new InvalidOperationException("Cannot publish document.");
    }
}

public class DraftState : DocumentState
{
    public override void Publish()
    {
        // do other stuff
        this.document.SetState(new ReviewingState());
    }
}

public class ReviewingState : DocumentState
{
    public override void Publish()
    {
        // do other stuff
        this.document.SetState(new PublishedState());
    }

    public override void Reject()
    {
        // do other stuff
        this.document.SetState(new DraftState());
    }
}

public class PublishedState : DocumentState
{
    public override void Retire()
    {
        // do other stuff
        this.document.SetState(new RetiredState());
    }
}

public class RetiredState : DocumentState
{
    public override void Publish()
    {
        this.document.SetState(new DraftState());
    }
}

public class Document
{
    private DocumentState state = new DraftState(); // Default initial state
    
    public void SetState(DocumentState state) 
    {
        this.state = state;
        this.state.SetDocument(this);
    }

    public void Publish()
    {
        this.state.Publish();
    }

    public void Reject()
    {
        this.state.Reject();
    }

    public void Retire()
    {
        this.state.Retire();
    }
}

This solves our two problems above: State-dependent logic is moved to classes of their own. Polymorphically changing the behavior of Document object according to the state it is in, thus avoiding all these if statements to check the state (so we won’t repeat ourselves). New state to implement? No problem, we just extend with a new DocumentState class and change one line on the existing states to switch to this new state. Et voilà!

The way it works is that Document now has a reference to DocumentState, an abstract class, which defines as virtual methods all three document operations that are state-dependent. Document class now has these three operations implemented as methods, so your code will call myDocument.Publish() which will in turn call State.Publish() delegating that operation to the state class.

In the code as in the diagram, there are two “special” methods: Document::SetState(DocumentState state) and DocumentState::SetDocument(Document document), these are used to maintain a context between the state transitions. Calling document.SetState(newState) will set the new state for the document and that in place will call State.SetDocument(this) to set the document (context) reference. You can find a working example here.

On the next post I will include an example of this applied using Entity Framework.

First Entry. Why.

Having used the internet for quite a long time and read more than one blog now time has come to start my own. There are plenty of blogs that have helped me getting answers and solve the issues I had at hand. So I decided is now time to give back.

In this blog I plan to write about Programming, Design Patterns, Agile, and other technology related subjects. Have comments? I’d love to read them and improve this blog.

Stay tuned.