State Pattern – schimbarea comportamentului în funcție de stare
În aplicațiile reale, obiectele rareori au un comportament fix. De cele mai multe ori, modul în care reacționează depinde de starea internă în care se află. De exemplu:
-
o comandă poate fi Nouă, Procesată, Livrată sau Anulată
-
un document poate fi Draft, Publicat sau Arhivat
-
un utilizator poate fi Activ, Suspendat sau Blocat
O abordare naivă ar presupune folosirea multor if / switch în cod, ceea ce duce rapid la:
-
cod greu de citit
-
încălcarea principiului Open/Closed
-
dificultăți mari la adăugarea de noi stări
State Pattern rezolvă elegant această problemă.
Ce este State Pattern?
State Pattern permite unui obiect să-și schimbe comportamentul atunci când starea internă se modifică. Obiectul va părea că și-a schimbat clasa.
Ideea principală este:
-
fiecare stare este reprezentată printr-o clasă separată
-
comportamentul specific stării este mutat în acea clasă
-
obiectul principal (contextul) delegă comportamentul către starea curentă
Problema: comportament dependent de stare
Să luăm un exemplu simplu: o comandă (Order).
Abordare greșită (anti-pattern)
public enum OrderStatus
{
New,
Paid,
Shipped,
Cancelled
}
public class Order
{
public OrderStatus Status { get; set; }
public void Process()
{
if (Status == OrderStatus.New)
{
Console.WriteLine("Procesare comandă nouă");
}
else if (Status == OrderStatus.Paid)
{
Console.WriteLine("Pregătire livrare");
}
else if (Status == OrderStatus.Shipped)
{
Console.WriteLine("Comanda a fost deja livrată");
}
else if (Status == OrderStatus.Cancelled)
{
throw new InvalidOperationException("Comanda este anulată");
}
}
}
❌ Probleme:
-
logică împrăștiată
-
metode care cresc necontrolat
-
modificări frecvente ale clasei
Order
Soluția: State Pattern
Structura pattern-ului
-
State – interfață comună pentru toate stările
-
ConcreteState – implementări concrete pentru fiecare stare
-
Context – obiectul care își schimbă comportamentul
Implementare în C#
1. Interfața IOrderState
public interface IOrderState
{
void Process(OrderContext context);
}
2. Stările concrete
Starea NewOrderState
public class NewOrderState : IOrderState
{
public void Process(OrderContext context)
{
Console.WriteLine("Procesare comandă nouă");
context.SetState(new PaidOrderState());
}
}
Starea PaidOrderState
public class PaidOrderState : IOrderState
{
public void Process(OrderContext context)
{
Console.WriteLine("Pregătire livrare");
context.SetState(new ShippedOrderState());
}
}
Starea ShippedOrderState
public class ShippedOrderState : IOrderState
{
public void Process(OrderContext context)
{
Console.WriteLine("Comanda a fost deja livrată");
}
}
Starea CancelledOrderState
public class CancelledOrderState : IOrderState
{
public void Process(OrderContext context)
{
throw new InvalidOperationException("Comanda este anulată");
}
}
3. Contextul OrderContext
public class OrderContext
{
private IOrderState _state;
public OrderContext(IOrderState initialState)
{
_state = initialState;
}
public void SetState(IOrderState state)
{
_state = state;
}
public void Process()
{
_state.Process(this);
}
}
4. Utilizare
var order = new OrderContext(new NewOrderState());
order.Process(); // New -> Paid
order.Process(); // Paid -> Shipped
order.Process(); // Shipped
Avantaje
✅ elimină if / switch
✅ respectă Open/Closed Principle
✅ fiecare stare este izolată și ușor de testat
✅ cod mai curat și extensibil
Dezavantaje
⚠️ număr mai mare de clase
⚠️ poate fi overkill pentru scenarii simple
Când să folosești State Pattern?
✔️ comportamentul se schimbă semnificativ în funcție de stare
✔️ ai multe tranziții între stări
✔️ vrei să eviți logică condițională complexă
❌ NU este recomandat când ai doar 1–2 stări simple
Diferența față de Strategy Pattern
| State | Strategy |
|---|---|
| Starea se schimbă din interior | Strategia se schimbă din exterior |
| Flux dependent de stare | Algoritmi interschimbabili |
Concluzie
State Pattern este ideal pentru modelarea proceselor reale (workflow-uri, comenzi, documente, plăți), unde comportamentul evoluează în timp.
Aplicat corect, duce la:
-
cod mai lizibil
-
mai puține bug-uri
-
extensibilitate reală