Obiektowe metody projektowania systemów Command Pattern
Wstęp: Command Pattern zawiera żądania jako obiekty, tym samym pozwala parametryzować użytkowników w zależności od ich żądań, ustawia się lub rejestruje żądania i pobiera operacje. Pozwala oddzielać wychodzenie od rozkazu z jego wykonania (opóźnionego działania). Pozwala oddzielać użytkowników interfejsu od działań które wykonują. Znany jest również jako Action lub Transaction.
Plan: 1.Cel stosowania 2.Przydatność 3.Struktura 4.Konsekwencja zastosowania 5.Przykładowy kod 6.Podsumowanie 7.Bibliografia
Cel stosowania Czasami jest konieczne przydzielać żądania do obiektów bez wiedzy o istnieniu jakichkolwiek operacji. Na przykład użytkownik interfejsu narzędzi zawiera obiekty jako przyciski i menu, które doprowadzają żądania odzewu do użytkownika wejściowego. Ale narzędzia nie mogą wykonywać rozkazów bezpośrednio w przycisku czy w menu, ponieważ tylko aplikacje, które używają narzędzi wiedzą co powinny zrobić z tym obiektem. Jako projektanci narzędzi nie mamy możliwości wiedzieć co odbiorca żądań lub operacji będzie chciał zrobić. Cel stosowania
Command pattern opisuje żądania nie sprecyzowanych obiektów aplikacji poprzez zwrot zadań do obiektów. Ten obiekt może być gromadzony i wpuszczany w obieg tak jak inne obiekty. Kluczem do tego wzoru jest abstrakcyjna klasa Command, w której deklarujemy interfejs do wykonywania operacji. W najprostszej formie ten interfejs zawiera abstrakcyjną operacje Execute. Konkretne podklasy Command precyzują akcje odbiorcy tworząc magazyn zadań i przygotowuje narzędzia do wykonywania zadań. Cel stosowania
Przydatność Command pattern używaj wszędzie tam gdy: parametryzujesz obiekty poprzez akcje które wykonują precyzujesz, ustawiasz w kolejce i wykonujesz żądania w różnym czasie chcesz mieć możliwość cofać akcje chcesz mieć możliwość rejestrowania zmian tak by mogły być one ponownie wykorzystane w przypadku gdy system przestanie działać chcesz budować system z wysokim poziomem operacji na operacjach prymitywnych
Struktura
Konsekwencje zastosowania Zastosowanie Command Pattern pociąga za sobą następujące konsekwencje: 1.Command odsprzęga obiekty tak by wywoływać operacje jedną z tych, z których wiesz jak jest wykonywana 2.Command ma klasy obiekty. One mogą być zmieniane i powiększane tak jak inne obiekty. 3.Możliwe jest gromadzenie Command-ów w zbiorowym Commmand-dzie 4.W bardzo prosty sposób można dodać nowe Command-y, ponieważ nie zmieniasz istniejących klas
Przykładowy kod class Command { public: virtual ~Command() ; virtual void Execute() = 0 ; protected: Command() ; };
Przykładowy kod class OpenCommand : public Command { public: OpenCommand(Application*) ; virtual void Execute() ; protected: virtual const char * AskUser() ; private: Application* _application ; char * _response ; };
Przykładowy kod OpenCommand::OpenCommand (Application * a) { _application = a ; }
Przykładowy kod void OpenCommand::Execute() { const char * name = AskUser() ; if (name != 0) { Document * document = new Document(name) ; _application->Add(document) ; document->Open() ; }
Przykładowy kod class PasteCommand : public Command { public: PasteCommand(Document*) ; virtual void Execute() ; private: Document* _document ; };
Przykładowy kod PasteCommand::PasteCommand (Document * doc) { _document = doc ; } void PasteCommand::Execute () { _document->Paste() ; }
Przykładowy kod template class SimpleCommand : public Command { public: typedef void (Receiver::*Action)() ; SimpleCommand(Receiver* r, Action a) : _receiver(r), _action(a) {} virtual void Execute() ; private: Action _action ; Receiver* _receiver; };
Przykładowy kod template void SimpleCommand ::Execute () { (_receiver->*_action)() ; } MyClass* receiver = new MyClass ; Command* aCommand = new SimpleCommand (receiver, &MyClass::Action) ; aCommand->Execute() ;
Przykładowy kod class MacroCommand : public Command { public: MacroCommand() ; virtual ~MacroCommand() ; virtual void Add(Command*) ; virtual void Remove(Command*) ; virtual void Execute() ; private: List * _cmds ; };
Przykładowy kod void MacroCommand::Execute () { ListIterator i(_cmds) ; for (i.First(); !i.IsDone(); i.Next()) { Command* c = i.CurrentItem() ; c->Execute() ; }
Przykładowy kod void MacroCommand::Add (Command * c) { _cmds->Append(c) ; } void MacroCommand::Remove (Command * c) { _cmds->Remove(c) ; }
Podsumowanie: Zawiera żądania jako obiekty Daje możliwość parametryzowania użytkowników w zależności od ich żądań Rejestruje zmiany
Przykład Commanda class Policz { public: void Podwoic( int& Wartosc ) { Wartosc *= 2; } }; class Command { public: virtual void Wykonaj( int& ) = 0; };
Przykład Commanda class SimpleCommand : public Command { typedef void (Policz::* Akcja)(int&); Policz* Odbiorca; Akcja Zadanie; public: SimpleCommand( Policz* rec, Akcja act ) { Odbiorca = rec; Zadanie = act; } virtual void Wykonaj( int& num ) { (Odbiorca->*Zadanie)( num ); } };
Przykład Commanda class MacroCommand : public Command { vector list; public: void add( Command* cmd ) { list.push_back( cmd ); } virtual void Wykonaj( int& num ) { for (int i=0; i < list.size(); i++) list[i]->Wykonaj( num ); } };
Przykład Commanda void main( void ) { Policz Objekt; Command* commands[3]; commands[0] = &SimpleCommand( &Objekt, &Policz::Podwoic ); MacroCommand two; two.add( commands[0] ); commands[1] = &two; MacroCommand four; four.add( &two ); commands[2] = &four; int num, index; while (true) { cout << "Wybierz zadanie (0=2x 1=4x 2=16x): "; cin >> num >> index; commands[index]->Wykonaj( num ); cout << " " << num << '\n'; }
Bibliografia: Stroustrup B.: The C++ Programming Language, Addison-Wesley, 2004 Gamma E., Helm R., Johnson R, Vlissides J.: Design patterns Elements of Reusable Object-Oriented Software, Addison Wesley
...i to by było tyle na dzisiaj!