Command Pattern – acțiuni tratate ca obiecte independente
În aplicațiile reale, acțiunile utilizatorilor sau ale sistemului pot deveni rapid complexe: salvare, anulare, refacere, programare de task-uri, macro-uri sau cozi de procesare. Dacă aceste acțiuni sunt implementate direct în UI sau în clasele care le declanșează, aplicația devine greu de extins și de întreținut.
Command Pattern rezolvă această problemă prin transformarea fiecărei acțiuni într-un obiect independent, care conține toate informațiile necesare pentru a fi executat. Astfel, cererea este complet decuplată de obiectul care o execută.
Ce problemă rezolvă Command Pattern?
Fără Command Pattern:
-
UI-ul cunoaște logica de business
-
acțiunile nu pot fi ușor refăcute (undo/redo)
-
este dificil să adaugi acțiuni noi fără a modifica cod existent
Cu Command Pattern:
-
acțiunile sunt obiecte de sine stătătoare
-
poți stoca, jurnaliza sau reexecuta comenzile
-
poți construi mecanisme de undo/redo sau macro-uri
Structura Command Pattern
Pattern-ul este format din patru componente principale:
-
Command – interfață sau clasă abstractă care definește metoda Execute()
-
ConcreteCommand – implementează comanda concretă
-
Receiver – obiectul care execută efectiv acțiunea
-
Invoker – obiectul care declanșează comanda
Opțional:
-
Undo() – pentru inversarea acțiunii
-
Command History – pentru stocarea comenzilor executate
Diagramă conceptuală (descriere)
Invoker → Command → Receiver
Invoker-ul nu știe ce face comanda, doar că o execută.
Exemplu simplu în C#
1. Interfața Command
public interface ICommand
{
void Execute();
}
2. Receiver – logica de business
public class Light
{
public void TurnOn()
{
Console.WriteLine("Lumina a fost aprinsă");
}
public void TurnOff()
{
Console.WriteLine("Lumina a fost stinsă");
}
}
3. Concrete Commands
public class TurnOnLightCommand : ICommand
{
private readonly Light _light;
public TurnOnLightCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOn();
}
}
public class TurnOffLightCommand : ICommand
{
private readonly Light _light;
public TurnOffLightCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOff();
}
}
4. Invoker
public class RemoteControl
{
private ICommand _command;
public void SetCommand(ICommand command)
{
_command = command;
}
public void PressButton()
{
_command.Execute();
}
}
5. Utilizare
var light = new Light();
var turnOn = new TurnOnLightCommand(light);
var turnOff = new TurnOffLightCommand(light);
var remote = new RemoteControl();
remote.SetCommand(turnOn);
remote.PressButton();
remote.SetCommand(turnOff);
remote.PressButton();
Implementarea Undo / Redo
Command Pattern devine extrem de puternic atunci când adăugăm suport pentru anulare.
Extinderea interfeței
public interface ICommand
{
void Execute();
void Undo();
}
Command cu Undo
public class TurnOnLightCommand : ICommand
{
private readonly Light _light;
public TurnOnLightCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOn();
}
public void Undo()
{
_light.TurnOff();
}
}
Istoric comenzi
public class CommandManager
{
private readonly Stack<ICommand> _history = new();
public void ExecuteCommand(ICommand command)
{
command.Execute();
_history.Push(command);
}
public void Undo()
{
if (_history.Any())
{
var command = _history.Pop();
command.Undo();
}
}
}
Exemple reale unde se folosește Command Pattern
-
Undo / Redo în editoare (Word, Photoshop)
-
Sisteme de meniuri și butoane
-
Task queues și job scheduling
-
Macro-uri (execuție în lanț de comenzi)
-
CQRS (Command side)
Avantaje
✔️ Decuplare completă între UI și business logic
✔️ Extensibilitate fără modificarea codului existent
✔️ Suport nativ pentru undo/redo
✔️ Testare ușoară a comenzilor individuale
Dezavantaje
❌ Crește numărul de clase
❌ Poate fi overkill pentru aplicații simple
Command Pattern vs Strategy Pattern
|
Command |
Strategy |
|---|---|
|
Reprezintă o acțiune |
Reprezintă un algoritm |
|
Poate fi stocată |
De obicei nu |
|
Suportă undo/redo |
Nu |
|
Executare punctuală |
Comportament pe termen lung |
Concluzie
Command Pattern este un pattern esențial pentru aplicații moderne și extensibile. Atunci când acțiunile devin obiecte independente, aplicația câștigă flexibilitate, testabilitate și control.
Dacă ai nevoie de undo/redo, cozi de procesare sau macro-uri, Command Pattern este alegerea potrivită.