Design patterns: State

in #design-patterns6 years ago

Today, about a fairly simple pattern called State, which serves to the very simple operation, as the name says to change the behavior of the object, when its internal state changes, i.e. when some event will happens, more accurately I will explain further in the article :)



statedesignpattern.jpg

Discussion

In the simplest terms, the state pattern is that if the internal state of the object changes (we can rely on one of the variables of this object, eg on the bool type) from truth to false then we change the behavior of this object.

The most important elements in implementing this pattern are:

  1. The Context class, which stores the current state of an object, overwrites this state and changes it.
  2. An abstract State class that has defined abstract methods to change the state of an object.
  3. The ConcreteState class, which inherits from the State class, implementing its methods which allow to change the state of the object.

The State pattern is used wherever there are any events, or the conditions to perform, sometimes is better to use the State pattern when we have a lot of complicated conditional statements then it is better to replace them with the State pattern. So in short, the State pattern can be used wherever there is complicated conditional logic. For example, we write an article as in wordpress and we have states of the Draft, Published article and so on, here you can use the State pattern.

stateconditions.jpg

Intent

  1. Changing the behavior of an object when its internal state changes.
  2. Minimizing the complexity of conditional statements.


Problem

You have to change the behavior of the object during the life of the so-called runtime program or you may have a set of quite complicated conditional statements, then a better solution will be to use the State pattern than several dozen conditional statements.


Use when:

  1. In large projects, you have to change the behavior of the object depending on its state (In small and medium-sized projects using the State pattern does not make sense, it only adds unnecessary complicated logic).
  2. You have a set of complicated conditional instructions.


Structure

Standard UML diagram of the State pattern:
State1.png

You already know what these classes are doing. StateOne, StateTwo and StateThree are of course the ConcreteState classes,which inherit from the State abstract class.

It is worth mentioning that the client operates with specific classes only from the level of the Context class.


Examples

Diagram of the State pattern in code

And we already know that in this piece of the article we are going to the real work 🙂

I will show in this example what is going on in the State pattern. Let’s start with the Context class.

namespace StateDesignPattern
{
    class Context
    {
        private State current;

        public void setState(State state)
        {
            current = state;
            Console.WriteLine("State: " + current.GetType().Name);
        }

        public void Request()
        {
            current.goNext(this);
        }
    }
}

We overwrite the state of the object with this class, store it and change it using the Request() method, passing the Context class to the specific class inheriting from the State abstract class using this method.

Let’s see what the State class looks like and the specific classes that inherit it.

First the State class.

namespace StateDesignPattern
{
    abstract class State
    {
        public abstract void goNext(Context context);
    }
}

There is nothing to understand in this class, we just defined the goNext() method to implement it in the specific StateOne, StateTwo and StateThree classes that inherit from the State class. And in order to not use a specific type class in the Context class.

Let’s see the specific classes inheriting from the State class.

First, the StateOne class.

namespace StateDesignPattern
{
    class StateOne : State
    {
        public override void goNext(Context context)
        {
            context.setState(new StateTwo());
        }
    }
}

StateTwo

namespace StateDesignPattern
{
    class StateTwo : State
    {
        public override void goNext(Context context)
        {
            context.setState(new StateThree());
        }
    }
}

StateThree

namespace StateDesignPattern
{
    class StateThree : State
    {
        public override void goNext(Context context)
        {
            context.setState(new StateOne());
        }
    }
}

We see that in each class with each call to the goNext() method, we change the state of the object in the Context class to another.

And of course, the client.

namespace StateDesignPattern
{
    class Program
    {
        static void Main()
        {
            Context c = new Context();
            c.setState(new StateOne());

            c.Request();
            c.Request();
            c.Request();
            c.Request();
            c.Request();

            Console.ReadKey();
        }
    }
}

In the client, we only create the Context object and set the initial state of the object further, only we change the state of this object using the Request() method.

Result:

StateOne.png

Bell states

Let’s do one more simple example with eg bell states.

Let’s start with the Context class, in this case Bell.

namespace Bellring
{
    class Bell
    {
        private BellState currentState;

        public Bell()
        {
            currentState = new BellRingingState();
        }

        public void setState(BellState state)
        {
            currentState = state;
        }

        public void alert()
        {
            currentState.Ring();
        }
    }
}

Now the State class, in this case the BellState class.

namespace Bellring
{
    abstract class BellState
    {
        public abstract void Ring();
    }
}

And specific classes inheriting from the BellState class, i.e. the BellRingingState class first.

namespace Bellring
{
    class BellRingingState : BellState
    {
        public override void Ring()
        {
            Console.WriteLine("Bell is ringing...");
        }
    }
}

And class BellSilentState.

namespace Bellring
{
    class BellSilentState : BellState
    {
        public override void Ring()
        {
            Console.WriteLine("Bell isn't ringing...");
        }
    }
}

And finally the customer.

namespace Bellring
{
    class Program
    {
        static void Main(string[] args)
        {
            Bell stateContext = new Bell();
            stateContext.alert();
            stateContext.alert();
            stateContext.setState(new BellSilentState());
            stateContext.alert();
            stateContext.alert();
            stateContext.alert();

            Console.ReadKey();
        }
    }
}

Result:

BellState.png

Real-life example

Vending machine

Let’s now make an example of a machine. Suppose you want to do business by putting machines on the road that can sell food products and you need a program that will change the state of the machine, eg when we deposit cash then we change the state of the machine from pending state to cash on the product release state.

State_example1.png

It is difficult to show the advantages of the State pattern in such simple examples, in such examples the State pattern adds only unnecessarily more code, it is used in bigger projects, but I will not make some long example then it will be hard to understand, but the following example should show how the State pattern is useful in large projects.

StateBoromir.jpg

We will do in the order as in the previous example.

Let’s start with the Context class, in this case it will be called VendingMachine.

namespace VendingMachine
{
    class VendingMachine
    {
        private VendingMachineState current;
        private int CostOfProduct;

        public void setState(VendingMachineState state)
        {
            current = state;
        }

        public void BuyProduct(int amount)
        {
            CostOfProduct = amount;
            current.GetProduct(amount, this);
        }

        public void TakeProduct()
        {
            current.GetProduct(CostOfProduct, this);
        }
    }
}

class VendingMachineState

namespace VendingMachine
{
    abstract class VendingMachineState
    {
        public abstract void ChangeVendingState(VendingMachine context);
        public abstract void GetProduct(int amount, VendingMachine context);
    }
}

And specific classes inheriting from the VendingMachineState class, which first is the VendingDepositeState class.

namespace VendingMachine
{
    class VendingDepositeState: VendingMachineState
    {
        public override void ChangeVendingState(VendingMachine context)
        {
            context.setState(new VendingStockState());
        }

        public override void GetProduct(int amount, VendingMachine context)
        {
            Console.WriteLine("Thrown into the machine: " + amount);

            if (amount==5)
            {
                Console.WriteLine("Payment accepted");
                ChangeVendingState(context);
            }
            else if(amount>=5)
            {
                int sum = amount - 5;
                Console.WriteLine("Payment accepted, pay the rest of money: " + sum);
                ChangeVendingState(context);
            }
            else
            {
                Console.WriteLine("Not enough money\n");
            }
        }
    }
}

In this class, we have the conditions that if the customer pays too much, we pay the rest to the customer and approve the payment, if not enough, we display the message.

First class VendingStockState, which we give the customer the product for which he paid.

namespace VendingMachine
{
    class VendingStockState: VendingMachineState
    {
        public override void ChangeVendingState(VendingMachine context)
        {
            context.setState(new VendingDepositeState());
        }

        public override void GetProduct(int amount, VendingMachine context)
        {
            Console.WriteLine("Money deliverd, amount: "+ amount + " give product which costing: 5\n");
            ChangeVendingState(context);
        }
    }
}

And finally the customer.

namespace VendingMachine
{
    class Program
    {
        static void Main(string[] args)
        {
            VendingMachine c = new VendingMachine();
            c.setState(new VendingDepositeState());

            c.BuyProduct(5);
            c.TakeProduct();

            c.BuyProduct(6);
            c.TakeProduct();

            c.BuyProduct(2);

            c.BuyProduct(8);
            c.TakeProduct();

            Console.ReadKey();
        }
    }
}

Result:

Stateexampletwo.png

Relations with other design patterns

  1. State pattern objects are often singletons.
  2. The Flyweight pattern can use the State pattern to divide objects.
  3. The interpreter can use the State pattern to define a Context class that parses all data.
    The State pattern is also similar to the Strategy pattern that will be discussed in the next article 🙂


Summary

That’s all about State🙂.

Link to github with the whole code from this article: https://github.com/Slaw145/State

This content also you can find on my steemit blog:

And on medium:

In the next article, we will talk about the Strategy pattern.

If you recognise it as useful, share it with others so that others can also use it.

Leave upvote and follow and wait for next articles :) .

– site on fb: Devman.pl-Slawomir Kowalski

– group on fb: DevmanCommunity

Ask, comment underneath at the end of the post, share it, rate it, whatever you want🙂.

Illustrations, pictures and diagrams are from: https://sourcemaking.com/design_patterns/state

Coin Marketplace

STEEM 0.20
TRX 0.14
JST 0.030
BTC 67629.79
ETH 3231.81
USDT 1.00
SBD 2.65