Паттерн State (Состояние) позволяет объекту изменять своё поведение в зависимости от внутреннего состояния. При этом создаётся впечатление, что объект меняет свой класс. Он инкапсулирует состояния в отдельные объекты, что упрощает добавление и изменение логики.
Когда применять State
- Когда поведение объекта зависит от его состояния, и это поведение нужно изменять во время выполнения.
- Когда в коде присутствуют громоздкие условные операторы (
if/elseилиswitch), зависящие от состояния. - Когда требуется добавить новые состояния без изменения логики основного класса.
PlantUML-диаграмма
// Интерфейс состояния
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 — мощный паттерн для управления поведением объектов, зависящим от их состояния. Он делает код более читаемым и масштабируемым, а также облегчает добавление новых режимов работы без изменения существующих классов.