
Состояние (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.AddPaper(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.AddPaper(-1);
}
else
{
_printer.SetState(_printer.PaperOffState);
_printer.PrintDocument();
}
}
public void AddPaper(int count)
{
_printer.AddPaper(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.AddPaper(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.AddPaper(-1);
_printer.SetState(_printer.WaitingState);
}
else
{
_printer.SetState(_printer.PaperOffState);
_printer.PrintDocument();
}
}
public void AddPaper(int count)
{
_printer.AddPaper(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 AddPaper(int count)
{
_countPaper += count;
}
}
Теперь выполним проверку:
var printer = new Printer();
printer.PowerOn();
printer.PrintDocument();
printer.AddPaper(3);
printer.PrintDocument();
printer.PrintDocument();
printer.PrintDocument();
printer.PrintDocument();
printer.PowerOff();
Полученный результат:
Это, конечно, не идеальный пример реализации данного паттерна, но его суть и принцип работы становится понятен.
Исходный код учебного проекта: https://github.com/flash2048/Patterns/tree/master/State