Understanding State Design Pattern

What is State DesignPattern?

The State Design Pattern allows an object to alter its behavior when its internal state changes. This behavioral design pattern encapsulates state-specific behavior into separate state objects and delegates state-dependent behavior to the current state object.

Real-World Application. ATM Machine

An ATM can be in various states: “NoCard”, “CardInserted”, “PinVerified”, and “TransactionCompleted”. Here’s how we can model this behavior using the State Design Pattern.

  • State Interface: This defines general ATM actions.
  • Concrete State Classes: Implementing the State interface for each state.
  • Context: This represents the ATM itself.
using System;

namespace StateDesignPattern
{
    public interface IATMState
    {
        void InsertCard();
        void EjectCard();
        void EnterPin();
        void WithdrawMoney();
    }

    public class NoCard : IATMState
    {
        public void InsertCard()
        {
            Console.WriteLine("Card Inserted. Please Enter the PIN.");
        }

        public void EjectCard()
        {
            Console.WriteLine("No Card to Eject.");
        }

        public void EnterPin()
        {
            Console.WriteLine("Please Insert Card First.");
        }

        public void WithdrawMoney()
        {
            Console.WriteLine("Insert and Verify Card to Withdraw Money.");
        }
    }

    public class CardInserted : IATMState
    {
        public void InsertCard()
        {
            Console.WriteLine("Card is Already Inserted.");
        }

        public void EjectCard()
        {
            Console.WriteLine("Card Ejected.");
        }

        public void EnterPin()
        {
            Console.WriteLine("PIN Accepted. You can now Withdraw Money.");
        }

        public void WithdrawMoney()
        {
            Console.WriteLine("Please Enter the PIN First.");
        }
    }

    public class PinVerified : IATMState
    {
        public void InsertCard()
        {
            Console.WriteLine("Card is Already Inserted.");
        }

        public void EjectCard()
        {
            Console.WriteLine("Card Ejected.");
        }

        public void EnterPin()
        {
            Console.WriteLine("PIN Already Verified.");
        }

        public void WithdrawMoney()
        {
            Console.WriteLine("Money Withdrawn. Card will be Ejected.");
        }
    }

    // Context
    public class ATM
    {
        private IATMState _currentState;

        public ATM()
        {
            // Default state
            _currentState = new NoCard();
        }

        public void SetState(IATMState state)
        {
            _currentState = state;
        }

        public void InsertCard()
        {
            _currentState.InsertCard();
        }

        public void EjectCard()
        {
            _currentState.EjectCard();
        }

        public void EnterPin()
        {
            _currentState.EnterPin();
        }

        public void WithdrawMoney()
        {
            _currentState.WithdrawMoney();
        }
    }

    // Test the State Pattern
    public class Program
    {
        public static void Main(string[] args)
        {
            ATM atm = new ATM();

            atm.InsertCard();  // Output: Card Inserted. Please Enter the PIN.

            atm.SetState(new CardInserted());
            atm.EnterPin();    // Output: PIN accepted. You can now Withdraw Money.

            atm.SetState(new PinVerified());
            atm.WithdrawMoney();  // Output: Money Withdrawn. Card will be Ejected.

            atm.EjectCard();   // Output: Card Ejected.
            Console.ReadKey();
        }
    }
}

This example shows an ATM’s typical actions, where the actions’ results change based on the machine’s current state. The State Design Pattern helps cleanly implement the changing behavior with the ATM’s state, making the code flexible and maintainable.

Output

Why Use StatePattern?

  • Manage Complexity: Simplifies complex conditional logic related to state transitions.
  • Maintainability: Easier to manage and extend state-specific behavior.
  • Encapsulation: Keeps state-specific behavior and state transitions encapsulated.

Where to Use?

  • Object Behavior Changes with State: An object's behavior varies significantly based on its state.
  • Clear State Transitions: When the state changes are well-defined and involve different behavior.
  • Reducing Conditional Logic: when you have a lot of if-else or switch-case statements based on state.

Conclusion

State Design Pattern enhances code maintainability and clarity by encapsulating state-specific behavior and transitions, making your application more robust and easier to manage.