stateСостояние (State) — это поведенческий шаблон проектирования, который позволяет объекту изменять своё поведения, в зависимости от внутреннего состояния.

Простейшая схема работы паттерна:Данный паттерн состоит из 3 блоков:

  • Класс, который будет менять своё поведение в зависимости от состояния.
  • Интерфейс, который должен реализовать каждое из конкретных состояний.
  • Классы конкретных состояний.

Представим, что мы пишем программу для принтера, которая должна распечатывать документ, посланный на печать. Принтер может быть в 4 состояниях: отключён, ожидание, печать, закончилась бумага. В режиме ожидания принтер ждёт, когда ему отправят документ, который необходимо распечатать. Если он уже печатает, то необходимо попросить подождать (поставить документ в очередь), если закончился тонер, то необходимо выдать предупреждение о невозможности печати. Сам принтер может выполнять 4 действий: включиться, отключиться и распечатать, пополняться бумагой.

Интерфейс IState будет иметь следующий вид.

interface IState
{
    void On();
    void Off();
    void Print();
    void AddPaper(int count);
}

Состояние отключённого питания:

class PowerOffState : IState
{
    private readonly Printer _printer;
 
    public PowerOffState(Printer printer)
    {
        _printer = printer;
    }
    public void On()
    {
        Console.WriteLine("Принтер включен");
        _printer.SetState(_printer.WaitingState);
    }
 
    public void Off()
    {
        Console.WriteLine("Принтер и так выключен");
    }
 
    public void Print()
    {
        Console.WriteLine("Принтер отключен, печать невозможна");
    }
 
    public void AddPaper(int count)
    {
        _printer.AddPater(count);
        Console.WriteLine("Бумага добавлена");
    }
}

Состояние ожидания печати:

class WaitingState : IState
{
    private readonly Printer _printer;
 
    public WaitingState(Printer printer)
    {
        _printer = printer;
    }
 
    public void On()
    {
        Console.WriteLine("Принтер уже и так включен");
    }
 
    public void Off()
    {
        Console.WriteLine("Принтер выключен");
    }
 
    public void Print()
    {
        if (_printer.CountPaper > 0)
        {
            Console.WriteLine("Сейчас всё распечатаем");
            _printer.AddPater(-1);
        }
        else
        {
            _printer.SetState(_printer.PaperOffState);
            _printer.PrintDocument();
        }
    }
 
    public void AddPaper(int count)
    {
        _printer.AddPater(count);
        Console.WriteLine("Бумага добавлена");
    }
}

Состояние отсутствия бумаги:

class PaperOffState : IState
{
    private readonly Printer _printer;
 
    public PaperOffState(Printer printer)
    {
        _printer = printer;
    }
 
    public void On()
    {
        Console.WriteLine("Принтер уже и так включен");
    }
 
    public void Off()
    {
        Console.WriteLine("Принтер выключен");
        _printer.SetState(_printer.PowerOffState);
    }
 
    public void Print()
    {
        if (_printer.CountPaper > 0)
        {
            _printer.SetState(_printer.PrintState);
            _printer.PrintDocument();
        }
        else
        {
            Console.WriteLine("Бумаги нет, печатать не буду");
        }
 
    }
 
    public void AddPaper(int count)
    {
        Console.WriteLine("Добавляем бумагу");
        _printer.AddPater(count);
        if (_printer.CountPaper > 0)
            _printer.SetState(_printer.WaitingState);
    }
}

Состояние печати:

class PrintState : IState
{
    private readonly Printer _printer;
 
    public PrintState(Printer printer)
    {
        _printer = printer;
    }
    public void On()
    {
        Console.WriteLine("Принтер уже и так включен");
    }
 
    public void Off()
    {
        Console.WriteLine("Принтер выключен");
    }
 
    public void Print()
    {
        if (_printer.CountPaper > 0)
        {
            Console.WriteLine("Идёт печать...");
            _printer.AddPater(-1);
            _printer.SetState(_printer.WaitingState);
        }
 
        else
        {
            _printer.SetState(_printer.PaperOffState);
            _printer.PrintDocument();
        }
 
    }
 
    public void AddPaper(int count)
    {
        _printer.AddPater(count);
        Console.WriteLine("Бумага добавлена");
    }
}

Осталось реализовать сам класс принтера. Он будет выглядеть следующим образом:

class Printer
{
    private IState _state;
    private int _countPaper;
 
    public PaperOffState PaperOffState { get; private set; }
    public PowerOffState PowerOffState { get; private set; }
    public PrintState PrintState { get; private set; }
    public WaitingState WaitingState { get; private set; }
    public int CountPaper {
        get { return _countPaper; }
    }
 
    public Printer()
    {
        PowerOffState = new PowerOffState(this);
        PaperOffState = new PaperOffState(this);
        PrintState = new PrintState(this);
        WaitingState = new WaitingState(this);
        _state = WaitingState;
    }
 
    public void SetState(IState state)
    {
        _state = state;
    }
 
    public void PrintDocument()
    {
        _state.Print();
    }
 
    public void PowerOff()
    {
        _state.Off();
    }
    public void PowerOn()
    {
        _state.On();
    }
 
    public void AddPater(int count)
    {
        _countPaper += count;
    }
}

Теперь выполним проверку:

var printer = new Printer();
printer.PowerOn();
printer.PrintDocument();
printer.AddPater(3);
printer.PrintDocument();
printer.PrintDocument();
printer.PrintDocument();
printer.PrintDocument();
printer.PowerOff();

 

Это, конечно, не идеальный пример реализации данного паттерна, но его суть и принцип работы становится понятен.