Incapsularea algoritmilor (Strategy)
Definitie
Presupune incapsularea separata a fiecarui algoritm dintr-o
familie, facand astfel ca algoritmii respectivi sa fie interschimbabili.
Sablonul se mai numeste politica (policy).
Context
Sa consideram ca exemplu un set de algoritmi destinati
structurarii unui text pe linii. Nu este recomandabil sa se includa acesti
algoritmi in clasele care au nevoie de ei, din urmatoarele motive:
In Composition va exista o referinta la Compositor. Ori de cate ori va fi necesara reformatarea textului, clasa Composition va delega clasa Compositor sa execute aceasta operatie. Spre exemplu, daca referinta la Compositor este cmpstr, in metoda Update va exista probabil un apel de forma:
Motivatii
Sablonul Strategy se va aplica atunci cand:
mai multe clase inrudite difera doar prin comportament;Solutie
sunt necesare mai multe variante ale unui algoritm, care difera intre ele, de exemplu, prin compromisul spatiu-timp adoptat;
un algoritm utilizeaza date pe care clientul algoritmului nu trebuie sa le cunoasca;
intr-o clasa sunt definite mai multe actiuni care apar ca structuri conditionale multiple. In loc de aceasta, se recomanda plasarea ramurilor conditionale inrudite in cate o clasa strategy separata.
In figura de mai jos este data structura de clase care constituie sablonul Strategy:
Consecinte
Ierarhiile de clase Strategy definesc familii de algoritmi sau comportamente care pot fi reutilizate in diverse contexte. Mostenirea poate fi aplicata aici pentru a "factoriza" functionalitatile comune ale algoritmilor.
Sablonul Strategy ofera o alternativa structurilor de control conditionale pentru selectia unui anumit comportament. Cand mai multi algoritmi sunt inglobati intr-o singura clasa, este greu sa se evite structurile conditionale. Adesea, prezenta in cod a numeroase asemenea structuri constituie un indiciu ca ar trebui aplicat sablonul Strategy.
Clasele Strategy se pot utiliza si in cazul producerii de implementari diferite ale aceluiasi comportament, clientul putand sa opteze pentru una dintre ele, in functie de performantele dorite.
Un dezavantaj al sablonului Strategy il consituie faptul ca, pentru a putea alege o anumita implementare sau un anumit algoritm, clientii trebuie sa stie care sunt ofertele si prin ce difera ele, una fata de alta. Acest lucru poate insemna in cele din urma ca un client sa trebuiasca sa cunoasca anumite detalii de implementare. De aceea, sablonul se va aplica doar in situatia in care diferentele relevante intre obiectele ConcreteStrategy se refera la comportamentul vizibil din perspectiva clientului.
Implementare
Interfetele Context si Strategy trebuie sa fie definite astfel incat sa permita claselor ConcreteStrategy un acces eficient la datele de care au nevoie din Context, si invers. Se pot adopta urmatoarele variante:Daca limbajul de implementare este C++ si suntem in situatia in care o anumita strategie urmeaza sa se stabileasca la compilare, iar in timpul executiei nu trebuie schimbata, atunci se pot folosi template-urile pentru definirea clasei Context:
- la apelul operatiilor din Strategy, Context sa paseze ca parametri datele necesare; in felul acesta clasele Strategy si Context raman decuplate, dar exista posibilitatea ca uneori Context sa trimita date de care Strategy nu are nevoie;
- obiectul Context sa se paseze pe sine ca parametru, la apelul unei operatii din Strategy sau clasa Strategy sa detina o referinta spre Context; in acest caz creste gradul de cuplaj intre cele doua clase, iar Context trebuie sa prezinte o interfata mai elaborata spre datele sale;
template <class AStrategy> class Context {
public:
void Operation( ) { theStrategy.DoAlgorithm( ); }
private:
AStrategy theStrategy;
//. . .
};
class MyStrategy {
public:
void DoAlgorithm( );
//. . .
};
Context <MyStrategy> aContext;In acest caz nu mai este necesara derivarea claselor ConcreteStrategy dintr-o clasa Strategy comuna.
Uneori clasa Context poate sa nu aiba nevoie de o anumita strategie si atunci este posibil ca referinta spre Strategy sa nu indice nici un obiect. In acest caz, va trebui testata aceasta referinta si daca ea nu indica nici un obiect, atunci se va executa o anumita secventa de operatii implicite. Astfel, clientii clasei Context vor trebui sa indice o strategie explicita doar daca nu ii satisface comportarea implicita:
class Context {
public:
void Operation( ) {
if (theStrategy != 0) theStrategy ->DoAlgorithm( );
else //fa ceva implicit
}
void SetStrategy(Strategy *s) { theStrategy = s; }
private:
Strategy *theStrategy;
//. . .
};
Tema
Se cere sa se aplice sablonul Strategy
pentru incapsularea algoritmilor de sortare a unui vector. Clasa cu rol
de Context va fi cea care modeleaza vectorul, iar ca algoritmi
de sortare vor fi luati in considerare: