Î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ă