State Design Pattern

The purpose of the State Design Pattern is allow an object to modify its behaviour when its internal state changes. Although this can often be solved with a boolean or using constants, this usually ends up with a lot of if-else, unreadable code and difficulty in maintenance.

An example of this is the following code:

class Program
    {
        static void Main(string[] args)
        {
            var payment = new Payment();
            payment.Id = 1;
            payment.PaymentState = PaymentStatus.Submitted;

            switch (payment.PaymentState)
            {
                case PaymentStatus.Submitted:
                    Console.WriteLine("Run complex logic A");
                case PaymentStatus.Approved:
                    Console.WriteLine("Run complex logic B");
                case PaymentStatus.Completed:
                    Console.WriteLine("Run complex logic C");

            }

        }
    }
 

In this case depending on the state of the payment we call some logic that typically is stored in a method. There are other issues with the naive switch-statement approach:

  • If we introduce a new state we have to extend every single switch statement to account for it
  • All actions for a particular state are spread around the actions: a change in one state action may have an effect on the other states
  • Difficult to unit test: each method can have a switch statement creating many permutations of the inputs and the corresponding outputs

The intent of the State Design Pattern is to decouple the state of the class in question. In certain occasions it is required that an object has different behaviours according to the state in which it is. This is complicated to handle, especially when you must take into account change of behaviour and state of that object, all within the same block of code. The State Design Pattern proposes a solution to this complication by creating one object for each possible state.

When to use it

  • The behaviour of an object depends on its state and must change its behaviour at runtime depending on that state.
  • When operations have long sentences with multiple branches depending on the state of the object.

Participants

StateDesignPattern_1

Context: maintains an instance with the current state.
State: defines interface for the behaviour associated to a particular state of the Context.
StateConcrete: Each subclass implements the behaviour associated with a context state.

The Context delegates the specific state to the current StateConcrete object. A Context object can be passed as a parameter to a State object. In this way the State class can access the context if necessary. Context is the main interface for the client. The client can configure a context with state objects. Once this is done these clients will not have to deal with State objects directly. Both the Context object and the StateConcreto objects can decide the state change.

Example

Imagine that we have a payment application that process payments depending on the state of the payment. Let’s solve the naive switch-statement approach that we took above.

1.Create the Payment Status Enum

StateDesignPattern_2

2. Create the IPayment State (State)

StateDesignPattern_3

This is the interface that all the states have to implement

3.Create the Payment class (Context)

StateDesignPattern_4

In this example the Payment class will act as the Context in the State Design Pattern. As you can see the Process Payment method delegated to the PaymentState object where the Payment object is completely oblivious of the actual state . It only sees the interface, i.e. an abstraction, which facilitates loose coupling and enhanced testability.

4.Creation of the Concrete States (submitted, approved, completed)

StateDesignPattern_5
StateDesignPattern_6
StateDesignPattern_7

5.Create the Client

StateDesignPattern_8

Conclusion

That’s it really, the state pattern is not more complicated than this.

We separated out the state-dependent logic to standalone classes that can be tested independently. It’s now easy to introduce new states later. We won’t have to extend dozens of switch statements – the new state object will handle that logic internally. The Payment object is no longer concerned with the concrete state objects – it delegates the logic to process a payment  to the states.

References

Code HERE

Thanks for reading. Happy Coding

Advertisements
This entry was posted in Architecture and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s