The Difference Between the Two GOF Patterns "Strategy" and "State"

The GOF Strategy and State patterns are remarkably similiar and it is really only a minor implementation detail that distinguishes the two.

 

Part 1. Strategy Pattern

 

The way I like to think of the strategy pattern is (1) encapsulation of a method into a class and (2) being able to swap out this method with different implementations. 

 

We'll often come across a situation when building code where we have long "if-then-else" chains in our class methods which determine how the method will behave. This is a perfect situation to refactor to a Strategy pattern, especially if we have cut-n-pasted the "if-then-else" chain to other methods. These chains can be replaced by a single strategy.

 

So, basically all we are doing is creating a class with "pluggable" functionality. For instance, if we have the following interface that defines a "Calculate" strategy:

 

interface ICalculator

{

    int Calculate(int inputA, int inputB);

}

 

And two implementations of the strategy:

 

class Adder : ICalculator

{ 

    #region ICalculator Members

 

    public int Calculate(int inputA, int inputB)

    {

        return inputA + inputB;

    }

 

    #endregion

}

 

class Subtractor : ICalculator

{ 

    #region ICalculator Members

 

    public int Calculate(int inputA, int inputB)

    {

        return inputA - inputB;

    }

 

    #endregion

}

 

We can create a class where we can "plug-in" either strategy to be used for execution of the "Calculate()" method below:

 

class NumberChanger

{

    public NumberChanger(ICalculator calculationStrategy)

    {

        m_calculationStrategy = calculationStrategy;

        m_state = 1;

    }

 

    private ICalculator m_calculationStrategy;

    private int m_state;

 

    public int State

    {

        get { return m_state; }

    }

 

    internal ICalculator CalculationStrategy

    {

        get { return m_calculationStrategy; }

        set { m_calculationStrategy = value; }

    }

 

    public int Calculate(int input)

    {

        m_state = m_calculationStrategy.Calculate(m_state, input);

        return m_state;

    }

}

 

This is how we would use the class:

 

StrategyPattern.NumberChanger strategyCalculator = new StrategyPattern.NumberChanger(new Adder());

while (strategyCalculator.State < 10)

{

    Console.WriteLine("StrategyPattern.NumberChanger.State = " + strategyCalculator.State);

    strategyCalculator.Calculate(1);    

}

 

strategyCalculator.CalculationStrategy = new Subtractor();

while (strategyCalculator.State > 0)

{

    Console.WriteLine("StrategyPattern.NumberChanger.State = " + strategyCalculator.State);

    strategyCalculator.Calculate(1);

}

 

And our result is:

 

StrategyPattern.NumberChanger.State = 1

StrategyPattern.NumberChanger.State = 2

StrategyPattern.NumberChanger.State = 3

StrategyPattern.NumberChanger.State = 4

StrategyPattern.NumberChanger.State = 5

StrategyPattern.NumberChanger.State = 6

StrategyPattern.NumberChanger.State = 7

StrategyPattern.NumberChanger.State = 8

StrategyPattern.NumberChanger.State = 9

StrategyPattern.NumberChanger.State = 10

StrategyPattern.NumberChanger.State = 9

StrategyPattern.NumberChanger.State = 8

StrategyPattern.NumberChanger.State = 7

StrategyPattern.NumberChanger.State = 6

StrategyPattern.NumberChanger.State = 5

StrategyPattern.NumberChanger.State = 4

StrategyPattern.NumberChanger.State = 3

StrategyPattern.NumberChanger.State = 2

StrategyPattern.NumberChanger.State = 1

 

Part 2. The State Pattern

 

The way I like to think of the State Pattern is simply a Strategy Pattern where the class manages it's own strategy based on it's state (thus the name).

 

If we have the same ICalculator, Adder, and Subtractor classes from above, we could use them in an implementation of the State pattern as follows (Note how the class sets its own strategy based on its state in the Calculate() method):

 

class NumberChanger

{

    public NumberChanger()

    {

        m_calculationStrategy = m_adder;

        m_state = 1;

    }

 

    private ICalculator m_calculationStrategy;

    private int m_state; 

    private static ICalculator m_adder = new Adder();

    private static ICalculator m_subtractor = new Subtractor();

 

    public int State

    {

        get { return m_state; }

    }

 

    internal ICalculator CalculationStrategy

    {

        get { return m_calculationStrategy; }

        set { m_calculationStrategy = value; }

    }

 

    public int Calculate(int input)

    {

        m_state = m_calculationStrategy.Calculate(m_state, input);

        // this is where the interal state is evaluated and

        // the strategy is set

        if (m_state >= 10)

        {

            m_calculationStrategy = m_subtractor;

        }

        else if (m_state < 1)

        {

            m_calculationStrategy = m_adder;

        } 

        return m_state;

    }

}

 

And our implementation looks like this:

 

StatePattern.NumberChanger stateCalculator = new StatePattern.NumberChanger();

while (stateCalculator.State > 0)

{

    Console.WriteLine("StatePattern.NumberChanger.State = " + stateCalculator.State);

    stateCalculator.Calculate(1);

}

 

For a similiar final result:

 

StatePattern.NumberChanger.State = 1

StatePattern.NumberChanger.State = 2

StatePattern.NumberChanger.State = 3

StatePattern.NumberChanger.State = 4

StatePattern.NumberChanger.State = 5

StatePattern.NumberChanger.State = 6

StatePattern.NumberChanger.State = 7

StatePattern.NumberChanger.State = 8

StatePattern.NumberChanger.State = 9

StatePattern.NumberChanger.State = 10

StatePattern.NumberChanger.State = 9

StatePattern.NumberChanger.State = 8

StatePattern.NumberChanger.State = 7

StatePattern.NumberChanger.State = 6

StatePattern.NumberChanger.State = 5

StatePattern.NumberChanger.State = 4

StatePattern.NumberChanger.State = 3

StatePattern.NumberChanger.State = 2

StatePattern.NumberChanger.State = 1

 

Conclusion.

 

I hope this article gives you a better idea of the state and strategy patterns and how closely they are related. Both these patterns are really a Strategy pattern at the core where the only difference is that the State pattern maintains its own strategy.

 

Until next time,

Happy Coding. 

X

Build smarter apps with Machine Learning, Bots, Cognitive Services - Start free.

Start Learning Now