Sunday, July 8, 2018

State Pattern


State design pattern is used to modify behavior of an object whenever internal state changes. State Pattern falls under behavioral pattern of GOF (Gang of Four) patterns.

When to use –


State pattern used when state of an object changes based on some action or condition at run-time without changing the interface. State pattern used to alter the behavior of an object as its internal state changes. This pattern used for some complex decision making program which represents multiple states.

Major components of State pattern –


Context – The clients of the State design pattern use the context class directly. Clients do not have access to state objects. Context class holds State objects that changes behavior according to its state.
State – This is an abstract class.
Concrete State – This class inherited from State class. This class provides real functionality that used by Context object. This class provides behavior to check and change state based on condition/action.

Let’s have a look on below example of State design pattern.

Code –
//state
public abstract class State
{
    public BankAccount Account { get; set; }
    public double Balance { get; set; }

    public abstract void Deposit(double amount);
    public abstract void Withdraw(double amount);
}
//concrete state
public class Normal : State
{
    public Normal(State state)
    {
        Balance = state.Balance;
        Account = state.Account;
    }
    public Normal(double balance, BankAccount account)
    {
        Balance = balance;
        Account = account;
    }
    public override void Deposit(double amount)
    {
        Balance += amount;
        CheckState();
    }

    public override void Withdraw(double amount)
    {
        Balance -= amount;
        CheckState();
    }
    void CheckState()
    {
        if (Balance > 1000)
            Account.State = new Classic(this);
    }
}
//concrete state
public class Classic : State
{
    public Classic(State state)
    {
        Balance = state.Balance;
        Account = state.Account;
    }
    public Classic(double balance, BankAccount account)
    {
        Balance = balance;
        Account = account;
    }
    public override void Deposit(double amount)
    {
        Balance += amount;
        CheckState();
    }
    public override void Withdraw(double amount)
    {
        Balance -= amount;
        CheckState();
    }
    void CheckState()
    {
        if (Balance < 1000)
            Account.State = new Normal(this);
        else if (Balance > 2000)
            Account.State = new Platinum(this);
    }
}
//concrete state
public class Platinum : State
{
    public Platinum(State state)
    {
        Balance = state.Balance;
        Account = state.Account;
    }
    public Platinum(double balance, BankAccount account)
    {
        Balance = balance;
        Account = account;
    }
    public override void Deposit(double amount)
    {
        Balance += amount;
        CheckState();
    }
    public override void Withdraw(double amount)
    {
        Balance -= amount;
        CheckState();
    }
    void CheckState()
    {
        if (Balance < 2000)
            Account.State = new Classic(this);
        else if (Balance < 1000)
            Account.State = new Normal(this);
    }
}
//context
public class BankAccount
{
    public State State { get; set; }
    public string Name { get; set; }
    public BankAccount(string name)
    {
        Name = name;
        State = new Normal(0, this);
    }
    public double Balance
    {
        get { return State.Balance; }
    }
    public void Deposit(double amount)
    {
        State.Deposit(amount);
        Console.WriteLine("Deposited - {0}", amount);
        Console.WriteLine("Balance - {0}", Balance);
        Console.WriteLine("Account Status - {0}", State.GetType().Name);
        Console.WriteLine(new string('-', 50));
    }
    public void Withdraw(double amount)
    {
        State.Withdraw(amount);
        Console.WriteLine("Withdrawn - {0}", amount);
        Console.WriteLine("Balance - {0}", Balance);
        Console.WriteLine("Account Status - {0}", State.GetType().Name);
        Console.WriteLine(new string('-', 50));
    }
}
class Program
{
    //entry point
    static void Main(string[] args)
    {
        BankAccount account = new BankAccount("Mitesh Sureja");
        account.Deposit(500);
        account.Deposit(600);
        account.Deposit(1000);
        account.Withdraw(500);
        account.Withdraw(1500);
        Console.Read();
    }

}

Output –


Above example demonstrates change in bank account status based on its remaining balance. In this example, BankAccount class is context class and Normal, Classic, Platinum are various classes of concrete state. The CheckState method of concrete state classes responsible to check condition and change states accordingly on each action like Deposit or Withdraw. Based on available balance in bank account, the status automatically changes between Normal, Classic and Platinum.


You can download full code from Gist.

I hope this article helps you to know more about State Design Pattern. Please leave your feedback in comments section below.

References –

See Also –


No comments:

Post a Comment