Documente Academic
Documente Profesional
Documente Cultură
1. Obiective
2. Scop și motivație
Șablonul Comandă încapsulează o funcție într-un obiect, astfel încât este posibilă trimiterea
unor funcții ca parametri, formarea de cozi de apeluri, înregistrarea listei de apeluri (engl.
“logging”) și asigurarea suportului pentru operațiile de anulare (engl. “undo”).
Să presupunem o aplicație cu o bară de instrumente care poate fi personalizată. Cu fiecare
buton al barei, utilizatorul își poate asocia funcția dorită. Deci, din punct de vedere al proiectantului,
nu se poate cunoaște apriori nici operația propriu-zisă care va fi efectuată de un buton și nici
obiectul concret care va realiza operația respectivă. De asemenea, pentru a avea o arhitectură
elegantă, toate butoanele trebuie tratate în mod unitar.
Soluția propusă de șablonul Comandă este încapsularea unui astfel de apel într-un obiect,
care poate fi stocat sau trimis ca parametru altor obiecte. După cum se poate vedea în figura 1, clasa
abstractă Command include o operație Execute, implementată de către clasele concrete derivate,
care conțin ca și câmp destinatarul apelului (obiectul care va realiza efectiv operația) și care îi vor
transfera acestuia apelul de execuție a operației. Pentru simplitate, s-au omis semnăturile metodelor.
Trebuie subliniat faptul că aceste clase de comandă sunt clase normale, care pot include și alte
câmpuri și metode pe lângă metoda Execute specifică șablonului.
1
Florin Leon, Ingineria programarii, http://florinleon.byethost24.com/lab_ip.htm
Florin Leon, Ingineria programarii - Laborator, http://florinleon.byethost24.com/lab_ip.htm
Figura 1. Exemplu de sistem bazat pe șablonul Comandă
Clasa MacroCommand este derivată la rândul său din aceeași clasă abstractă Command,
deci poate fi tratată ca orice altă comandă. Ea nu are însă un destinatar explicit, deoarece comenzile
din serie au propriul destinatar.
În măsura în care sistemul dispune de mai multe comenzi, dacă gestionarea acestora ar
aparține exclusiv clientului, acest fapt ar conduce la creșterea cuplării între aceste clase. De aceea,
șablonul introduce o clasă suplimentară, Invoker, care este responsabilă de setarea dinamică a
comenzilor și de apelarea acestora. În exemplul anterior, bara de instrumente este un Invoker, care
gestionează comenzile asociate cu butoanele sale, iar aplicația (clasa corespunzătoare ferestrei
principale, de exemplu) este clientul. Odată ce o comandă a fost introdusă în Invoker, ea poate fi
executată (o dată sau de mai multe ori), poate fi eliminată sau înlocuită în mod dinamic.
2
Florin Leon, Ingineria programarii, http://florinleon.byethost24.com/lab_ip.htm
Una din trăsăturile foarte importante care pot fi implementate ușor cu ajutorul șablonului
Comandă este asigurarea operațiilor de anulare (engl. “undo”) și refacere (engl. “redo”) a ultimelor
acțiuni.
În general, chiar dacă aplicația are mai multe comenzi, există câte o singură opțiune de
anulare, respectiv refacere. De aceea, sistemul trebuie să cunoască succesiunea operațiilor efectuate
și modul în care starea sistemului este modificată de fiecare comandă. Astfel, clasele de comandă
vor avea două metode suplimentare, Undo și Redo și vor trebui să rețină starea sistemului înainte de
Clasa Invoker poate reține într-o stivă succesiunea de comenzi efectuate iar dacă se dorește
de exemplu anularea ultimelor n acțiuni, se va apela metoda Undo din cele n elemente scoase din
stivă.
Putem spune că șablonul decuplează obiectul care invocă o operație de cel care știe cum să o
efectueze, ceea ce oferă un grad ridicat de flexibilitate în proiectarea unei aplicații. În cazul
interfeței grafice cu utilizatorul, o aplicație poate furniza pentru o funcție atât o interfață cu meniuri,
cât și una cu butoane și în acest caz meniurile și butoanele vor fi asociate cu aceleași instanțe ale
claselor concrete de comandă. Comenzile pot fi înlocuite în mod dinamic, lucru util pentru
implementarea meniurilor sensibile la context. De asemenea, prin compunerea comenzilor într-
unele de mai mari dimensiuni, se poate facilita generarea script-urilor de comandă.
3. Aplicabilitate
Șablonul Comandă poate fi utilizat în primul rând atunci când trebuie să se trimită o acțiune
ca parametru unui obiect, așa cum este cazul în situația cu bara de instrumente prezentată anterior.
Trimiterea poate fi făcută și prin intermediul unei funcții de apelare inversă (engl. “callback”). De
exemplu, codul de tratare a evenimentelor în platforma .NET este încapsulat în funcții delegat și
utilizat sub forma:
3
Florin Leon, Ingineria programarii, http://florinleon.byethost24.com/lab_ip.htm
comenzilor simplifică foarte mult procesul, deoarece un fir de execuție nu trebuie să știe decât că o
activitate are o metodă Execute care trebuie apelată.
După cum am menționat, o comandă poate avea posibilitatea anulării sau refacerii acțiunii.
Dacă sistemul trebuie să permită anularea și refacerea unei întregi succesiuni de comenzi, acest fapt
poate fi ușor implementat prin introducerea în Invoker a unor liste (sau stive) de comenzi pentru
anulare, respectiv refacere. În momentul când se anulează o operație, comanda respectivă se scoate
din lista comenzilor de anulare și se introduce în lista comenzilor de refacere. Se procedează analog
4. Analiza șablonului
Clasa abstractă (sau interfața) Command declară metode precum Execute, eventual Undo și
Redo. Clasele concrete ConcreteCommand implementează operația Execute prin invocarea
operației sau operațiilor corespunzătoare din obiectul Receiver. Clasa Receiver știe cum să efectueze
operațiile asociate cu executarea unei comenzi. Orice clasă poate servi drept destinatar. Clasa
Invoker cere comenzii să execute acțiunea. Clientul creează un obiect ConcreteCommand și îi
stabilește destinatarul.
Modul în care acționează participanții este detaliat în diagrama de secvențe din figura 5.
4
Florin Leon, Ingineria programarii, http://florinleon.byethost24.com/lab_ip.htm
Florin Leon, Ingineria programarii - Laborator, http://florinleon.byethost24.com/lab_ip.htm
Figure 5. Diagrama de secvențe a șablonului Comandă
5. Exemplu de implementare
Comanda abstractă
Comanda concretă
5
Florin Leon, Ingineria programarii, http://florinleon.byethost24.com/lab_ip.htm
public override void Execute()
{
_receiver.Action();
}
}
Destinatarul
Invocatorul
class Invoker
{
private Command _command;
Clientul
6
Florin Leon, Ingineria programarii, http://florinleon.byethost24.com/lab_ip.htm
6. Aplicații
6.1. Realizați un program care simulează o foaie de calcul (gen Microsoft Excel). Un schelet
al aplicației pentru construirea interfeței grafice cu utilizatorul (prezentată în figura 6) este inclus
după observații și recomandări.
Observații:
Controlul de tip grid este realizat dinamic cu text-box-uri. Alternativ, se poate lucra cu un
control DataGridView;
Componentele controlului sunt de fapt de tip ExtendedTextBox, clasă derivată din TextBox.
Atunci când se editează textul unui ExtendedTextBox, în momentul în care se apasă ENTER
sau se părăsește controlul respectiv, deci când se trimite comanda, Text-ul este deja
modificat. Pentru a păstra într-un mod mai convenabil starea anterioară, se poate folosi
proprietatea PreviousText. Cum ar putea fi proiectată comanda de modificare a textului unei
celule pentru a nu fi nevoiți să folosiți această proprietate?
Rămâne la latitudinea proiectantului să stabilească gradul de complexitate al comenzilor –
dacă o comandă acționează asupra unui TextBoxGrid sau asupra unui ExtendedTextBox. De
obicei, se recomandă utilizarea unor comenzi cât mai simple.
Recomandări:
Se poate lucra cu trei clase de comenzi, pentru schimbarea textului, schimbarea formatului și
schimbarea culorii unui ExtendedTextBox;
Comanda va primi în constructor o referință la ExtendedTextBox-ul asupra căruia se fac
modificările (acesta este Receiver-ul).
Pentru a vă putea concentra exclusiv asupra aplicării șablonului, în continuare se dau câteva
clase ce urmează a fi completate sau utilizate în program.
7
Florin Leon, Ingineria programarii, http://florinleon.byethost24.com/lab_ip.htm
6.2. (opțional) Extindeți programul astfel încât să permită operații simple cu numerele din
celule. Numerele vor fi afișate cu două zecimale și vor fi aliniate la dreapta (figura 7).
La apăsarea tastei ENTER în celula D1, conținutul său va deveni „5.00”. Operații permise
vor fi: Add (adunare), Sub (scădere), Mul (înmulțire), Div (împărțire), cu exact 2 parametri. Se va
afișa un mesaj de eroare dacă celulele trimise ca argumente nu conțin numere valide. Când cursorul
revine în celula D1, deci când reîncepe editarea celulei, va apărea din nou formula. Rezultatul
operației apare numai când celula nu este selectată. Se vor păstra operațiile de anulare (“undo”) și
refacere (“redo”).
8
Florin Leon, Ingineria programarii, http://florinleon.byethost24.com/lab_ip.htm