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
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
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
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 Receiver>
class SimpleCommand : public Command {
public:
typedef void (Receiver::*Action)() ;
SimpleCommand(Receiver* r, Action a) : _receiver(r), _action(a) {}
virtual void Execute() ;
Przykładowy kod
template <class Receiver>
void SimpleCommand<Receiver>::Execute () {
(_receiver->*_action)() ; }
MyClass* receiver = new MyClass ; Command* aCommand = new
SimpleCommand<MyClass>(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() ;
Przykładowy kod
void MacroCommand::Execute () {
ListIterator<Command*> 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<Command*> list;
public:
void add( Command* cmd ) {
list.push_back( cmd );
}
virtual void Wykonaj( int& num ) {
for (int i=0; i < list.size(); i++)
Przykład Commanda
void main( void ) {
Policz Objekt;
Command* commands[3];
commands[0] = &SimpleCommand( &Objekt, &Policz::Podwoic );
MacroCommand two;
two.add( commands[0] );
two.add( commands[0] );
commands[1] = &two;
MacroCommand four;
four.add( &two );
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