/ janturon.cz / Od kodéra k analytikovi / Command a Strategy

Command a Strategy

Command

Command je vzor pro provedení operace nějakou metodou. Mějme dvě třídy (Receivers), nad jejichž metodami budeme toto provádět, pročež obě implementují rozhraní Command vnucující metodu execute():

struct Command { virtual void execute()=0; }; class StringReceiver : public Command { string data; public: StringReceiver(string word) { data = word; } void execute() { cout << " string:" << data; } }; class IntReceiver : public Command { int data; public: IntReceiver(int word) { data = word; } void execute() { cout << " int:" << data; } };

Operace nad těmito metodami provádí třída označovaná jako Invoker, která agreguje objekty typu Command. Zde například realizuje funkčnost Zpět pomocí historie příkazů:

class Invoker { vector<Command*> history; public: Invoker* Do(Command* c) { history.push_back(c); c->execute(); return this; } Invoker* Undo() { if(history.empty()) return this; history.back()->execute(); history.pop_back(); return this; } };

Třída aplikuje techniku řetězení metod (method chaining, není závislá na návrhovém vzoru), kde metoda účastnící se řetězení vrací objekt sama sebe, což umožňuje následující zápis:

int main() { Command *i = new IntReceiver(42), *s1 = new StringReceiver("raz"), *s2 = new StringReceiver("dva"); Invoker *invoker = new Invoker(); cout << "DOING:"; // int:42 string:raz int:42 string:dva invoker->Do(i)->Do(s1)->Do(i)->Do(s2); cout << endl << "UNDOING:"; // string:dva int:42 string:raz int:42 invoker->Undo()->Undo()->Undo()->Undo()->Undo(); // jedno undo navíc nezpůsobí podtečení zásobníku return 0; }

Strategy

Vzor Command zapouzdřuje metodu. Vzor Strategy jde o krok dále tím, že zapouzdřuje celý algoritmus.

Lidé používají různé algoritmy pro zdravení lidí: bohatým podlézají, chudými pohrdají. Pro pozdrav používají návrhový vzor Strategy.

struct Strategy { virtual string greetings()=0; }; struct HelloRich : Strategy { string greetings() { return "Hello, sir "; } }; struct HelloPoor : Strategy { string greetings() { return "Hello, citizen "; } }; class Human { Strategy *rich, *poor; public: property<string> name; property<int> salary; Human(string name, int salary) : name(name), salary(salary) { rich = new HelloRich(); poor = new HelloPoor(); } string Hello() { if(salary<30000) { return poor->greetings()+name.getValue()+"!"; } else { return rich->greetings()+name.getValue()+"!"; } } };

Postoje jsou zde řešeny samostatně (v třídách HelloRich a HelloPoor), což je princip vzoru Strategy.

int main() { Human john("John",20000); cout << john.Hello(); john.salary = 40000; cout << endl; cout << john.Hello(); return 0; }

Typičtěji se Strategy používá spolu s databází: záznam může být zpracován různě v závislosti na hodnotách jeho parametrů.

Strategy připomíná Command: HelloRich a HelloPoor hrají roli Receivers, Human roli Invoker. Zatímco ve vzoru Command předáváme Receiver přes parametr, Strategy se autonomně rozhodne, kterou z možností použije.

Strategy je syntakticky stejný jako Most, liší se ve způsobu použití: zatímco Most slouží k překlenutí rozdílu mezi verzemi (a před a po použití funguje exkluzivně jedna možnost), Strategy střídá verze podle svého rozhodovacího algoritmu.