State (Состояние)

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

Когда применять State

  • Когда поведение объекта зависит от его состояния, и это поведение нужно изменять во время выполнения.
  • Когда в коде присутствуют громоздкие условные операторы (if/else или switch), зависящие от состояния.
  • Когда требуется добавить новые состояния без изменения логики основного класса.

PlantUML-диаграмма

UML диаграмма State

// Интерфейс состояния
public interface IState
{
    void InsertCoin();
    void Dispense();
}

// Контекст — автомат с напитками
public class VendingMachine
{
    private IState _state;

    public VendingMachine()
    {
        _state = new NoCoinState(this);
    }

    public void SetState(IState state) => _state = state;

    public void InsertCoin() => _state.InsertCoin();
    public void Dispense() => _state.Dispense();
}

// Состояние: нет монеты
public class NoCoinState : IState
{
    private VendingMachine _machine;

    public NoCoinState(VendingMachine machine)
    {
        _machine = machine;
    }

    public void InsertCoin()
    {
        Console.WriteLine("Монета вставлена. Можно выбрать напиток.");
        _machine.SetState(new HasCoinState(_machine));
    }

    public void Dispense()
    {
        Console.WriteLine("Вставьте монету перед выбором напитка.");
    }
}

// Состояние: монета вставлена
public class HasCoinState : IState
{
    private VendingMachine _machine;

    public HasCoinState(VendingMachine machine)
    {
        _machine = machine;
    }

    public void InsertCoin()
    {
        Console.WriteLine("Монета уже вставлена. Выберите напиток.");
    }

    public void Dispense()
    {
        Console.WriteLine("Выдан напиток. Спасибо!");
        _machine.SetState(new NoCoinState(_machine));
    }
}

// Клиентский код
class Program
{
    static void Main()
    {
        var machine = new VendingMachine();

        machine.Dispense();     // Нет монеты
        machine.InsertCoin();   // Вставляем монету
        machine.InsertCoin();   // Попытка второй монеты
        machine.Dispense();     // Получаем напиток
    }
}
type State interface {
	InsertCoin()
	Dispense()
}

type VendingMachine struct {
	state State
}

func NewVendingMachine() *VendingMachine {
	m := &VendingMachine{}
	m.state = NoCoinState{machine: m}
	return m
}

func (m *VendingMachine) SetState(s State) { m.state = s }

func (m *VendingMachine) InsertCoin() { m.state.InsertCoin() }
func (m *VendingMachine) Dispense()   { m.state.Dispense() }

type NoCoinState struct {
	machine *VendingMachine
}

func (s NoCoinState) InsertCoin() {
	fmt.Println("Монета вставлена. Можно выбрать напиток.")
	s.machine.SetState(HasCoinState{machine: s.machine})
}

func (s NoCoinState) Dispense() {
	fmt.Println("Вставьте монету перед выбором напитка.")
}

type HasCoinState struct {
	machine *VendingMachine
}

func (s HasCoinState) InsertCoin() {
	fmt.Println("Монета уже вставлена. Выберите напиток.")
}

func (s HasCoinState) Dispense() {
	fmt.Println("Выдан напиток. Спасибо!")
	s.machine.SetState(NoCoinState{machine: s.machine})
}

func main() {
	machine := NewVendingMachine()

	machine.Dispense()
	machine.InsertCoin()
	machine.InsertCoin()
	machine.Dispense()
}
from typing import Protocol


class State(Protocol):
    def insert_coin(self) -> None: ...
    def dispense(self) -> None: ...


class VendingMachine:
    def __init__(self) -> None:
        self.state: State = NoCoinState(self)

    def insert_coin(self) -> None:
        self.state.insert_coin()

    def dispense(self) -> None:
        self.state.dispense()


class NoCoinState:
    def __init__(self, machine: VendingMachine) -> None:
        self._machine = machine

    def insert_coin(self) -> None:
        print("Монета вставлена. Можно выбрать напиток.")
        self._machine.state = HasCoinState(self._machine)

    def dispense(self) -> None:
        print("Вставьте монету перед выбором напитка.")


class HasCoinState:
    def __init__(self, machine: VendingMachine) -> None:
        self._machine = machine

    def insert_coin(self) -> None:
        print("Монета уже вставлена. Выберите напиток.")

    def dispense(self) -> None:
        print("Выдан напиток. Спасибо!")
        self._machine.state = NoCoinState(self._machine)


machine = VendingMachine()

machine.dispense()
machine.insert_coin()
machine.insert_coin()
machine.dispense()
interface State {
    insertCoin(): void;
    dispense(): void;
}

class VendingMachine {
    private state: State;

    constructor() {
        this.state = new NoCoinState(this);
    }

    setState(state: State): void {
        this.state = state;
    }

    insertCoin(): void {
        this.state.insertCoin();
    }

    dispense(): void {
        this.state.dispense();
    }
}

class NoCoinState implements State {
    constructor(private readonly machine: VendingMachine) {}

    insertCoin(): void {
        console.log("Монета вставлена. Можно выбрать напиток.");
        this.machine.setState(new HasCoinState(this.machine));
    }

    dispense(): void {
        console.log("Вставьте монету перед выбором напитка.");
    }
}

class HasCoinState implements State {
    constructor(private readonly machine: VendingMachine) {}

    insertCoin(): void {
        console.log("Монета уже вставлена. Выберите напиток.");
    }

    dispense(): void {
        console.log("Выдан напиток. Спасибо!");
        this.machine.setState(new NoCoinState(this.machine));
    }
}

const machine = new VendingMachine();

machine.dispense();
machine.insertCoin();
machine.insertCoin();
machine.dispense();
interface State {
    void insertCoin();
    void dispense();
}

class VendingMachine {
    private State state;

    VendingMachine() {
        state = new NoCoinState(this);
    }

    void setState(State state) {
        this.state = state;
    }

    void insertCoin() {
        state.insertCoin();
    }

    void dispense() {
        state.dispense();
    }
}

class NoCoinState implements State {
    private final VendingMachine machine;

    NoCoinState(VendingMachine machine) {
        this.machine = machine;
    }

    public void insertCoin() {
        System.out.println("Монета вставлена. Можно выбрать напиток.");
        machine.setState(new HasCoinState(machine));
    }

    public void dispense() {
        System.out.println("Вставьте монету перед выбором напитка.");
    }
}

class HasCoinState implements State {
    private final VendingMachine machine;

    HasCoinState(VendingMachine machine) {
        this.machine = machine;
    }

    public void insertCoin() {
        System.out.println("Монета уже вставлена. Выберите напиток.");
    }

    public void dispense() {
        System.out.println("Выдан напиток. Спасибо!");
        machine.setState(new NoCoinState(machine));
    }
}

public class Program {
    public static void main(String[] args) {
        VendingMachine machine = new VendingMachine();

        machine.dispense();
        machine.insertCoin();
        machine.insertCoin();
        machine.dispense();
    }
}
interface State {
    fun insertCoin()
    fun dispense()
}

class VendingMachine {
    var state: State = NoCoinState(this)

    fun insertCoin() = state.insertCoin()
    fun dispense() = state.dispense()
}

class NoCoinState(private val machine: VendingMachine) : State {
    override fun insertCoin() {
        println("Монета вставлена. Можно выбрать напиток.")
        machine.state = HasCoinState(machine)
    }

    override fun dispense() {
        println("Вставьте монету перед выбором напитка.")
    }
}

class HasCoinState(private val machine: VendingMachine) : State {
    override fun insertCoin() {
        println("Монета уже вставлена. Выберите напиток.")
    }

    override fun dispense() {
        println("Выдан напиток. Спасибо!")
        machine.state = NoCoinState(machine)
    }
}

fun main() {
    val machine = VendingMachine()

    machine.dispense()
    machine.insertCoin()
    machine.insertCoin()
    machine.dispense()
}

Плюсы и минусы

Плюсы Минусы
Избавляет от громоздких условных операторов Увеличивает количество классов
Упрощает добавление новых состояний Контекст должен знать о переходах между состояниями
Разделяет ответственность: каждое состояние отвечает за своё поведение Иногда сложно определить оптимальную гранулярность состояний

Заключение

State — мощный паттерн для управления поведением объектов, зависящим от их состояния. Он делает код более читаемым и масштабируемым, а также облегчает добавление новых режимов работы без изменения существующих классов.