FactoryMethod Decorator

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:

Aceste probleme pot fi evitate prin definirea unor clase separate care sa incapsuleze fiecare cate un algoritm din multimea considerata (in cazul nostru, algoritmii de structurare a textului). Un algoritm incapsulat in acest mod se numeste strategie.
Presupunem ca o clasa Composition are ca sarcina gestionarea si actualizarea liniilor de text afisate intr-o fereastra de editare. Strategiile de defalcare a textului pe linii vor fi implementate nu in Composition, ci in cate o clasa separata, derivata dintr-o superclasa comuna - Compositor. Structura de clase este cea din figura de mai jos:

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:

cmpstr -> Compose( );
Un client al clasei Composition va specifica algoritmul pe care il doreste, transmitand spre Composition referinta obiectului derivat din Compositor adecvat. In acest scop, clasa Composition va primi referinta respectiva ca parametru in constructor si/sau va include metode prin care clientul sa poata seta membrul cmpstr.
In exemplul considerat avem 3 algoritmi de formatare posibili:


Motivatii

Sablonul Strategy se va aplica atunci cand:

mai multe clase inrudite difera doar prin comportament;
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.
Solutie

In figura de mai jos este data structura de clase care constituie sablonul Strategy:

Strategy si Context interactioneaza pentru a implementa un algoritm ales. Un obiect Context poate transmite spre Strategy toate datele necesare algoritmului, cand acesta este apelat; sau, obiectul Context se poate transmite pe sine insusi ca argument al operatiilor din Strategy.
Obiectul Context dirijeaza cererile clientilor sai spre obiectul Strategy de care este legat. De regula, clientii creaza si paseaza spre Context obiecte de tip ConcreteStrategy, dupa care clientii vor interactiona doar cu Context.
 

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:
 
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:

Elementele vectorului vor fi obiecte asupra carora se pot aplica operatorii de comparare.

FactoryMethod Decorator