Пример кода
Приведенный ниже код на языке C++ дает представление о реализации классов Command, обсуждавшихся в разделе Мотивация. Мы определим классы OpenCommand, PasteCommand и MacroCommand. Сначала абстрактный класс Command:
class Command { public: virtual ~Command (); virtual void Execute () = 0; protected: Command (); };
Команда OpenCommand открывает документ, имя которому задает пользователь. Конструктору OpenCommand передается объект Application. Функция AskUser запрашивает у пользователя имя открываемого документа:
class OpenCommand: public Command { public: OpenCommand (Application*); virtual void Execute (); protected: virtual const char* AskUser (); private: Application* _application; char* _response; }; OpenCommand::OpenCommand (Application* a) { _application = a; }
void OpenCommand::Execute () { const char* name = AskUser(); if (name!= 0) { Document* document = new Document(name); _application->Add(document); document->0pen(); } }
Команде PasteCommand в конструкторе передается объект Document, являющийся получателем:
class PasteCommand: public Command { public: PasteCommand(Document*); virtual void Execute(); private: Document* document; };
PasteCommand::PasteCommand (Document* doc) { _document = doc; }
void PasteCommand::Execute () { _document->Paste(); }
В случае с простыми командами, не допускающими отмены и не требующими аргументов, можно воспользоваться шаблоном класса для параметризации получателя. Определим для них шаблонный подкласс SimpleCoiranand, который параметризуется типом получателя Receiver и хранит связь между объектом-получателеми действием, представленным указателем на функцию-член:
template <class Receiver> class SimpleCoiranand: public Command { public: typedef void (Receiver::* Action)(); SimpleCommand(Receiver* r, Action a): _receiver(r), _action(a) { } virtual void ExecuteO; private: Action _action; Receiver* _receiver; };
Конструктор сохраняет информацию о получателе и действии в соответствующих переменных экземпляра. Операция Execute просто выполняет действие по отношению к получателю:
template <class Receiver> void SimpleCommand<Receiver>::Execute () { (_receiver->*_action)(); }
Чтобы создать команду, которая вызывает операцию Action для экземпляра класса MyClass, клиент пишет следующий код:
MyClass* receiver = new MyClass; //... Command* aCommand = new SimpleCommand<MyClass>(receiver, &MyClass::Action); //... aCommand->Execute();
Имейте в виду, что такое решение годится только для простых команд. Для более сложных команд, которые отслеживают не только получателей, но и аргументы и, возможно, состояние, необходимое для отмены операции, приходится порождать подклассы от класса Command.
Класс MacroCommand управляет выполнением последовательности подкоманд и предоставляет операции для добавления и удаления подкоманд. Задавать получателя не требуется, так как в каждой подкоманде уже определен свой получатель:
class MacroCommand: public Command { public: MacroCommand(); virtual -MacroCommand(); virtual void Add(Command*); virtual void Remove(Command*); virtual void Execute(); private: List<Command*>* _cmds; };
Основой класса MacroCommand является его функция-член Execute. Она обходит все подкоманды и для каждой вызывает ее операцию Execute:
void MacroCommand::Execute () { ListIterator<Command*> i(_cmds); for (i.First();!i.IsDone(); i.Next()) { Command* с = i.Currentltem(); c->Execute(); } }
Обратите внимание, что если бы в классе MacroCommand была реализована операция отмены Unexecute, то при ее выполнении подкоманды должны были бы отменяться в порядке, обратном порядке тому, который применяется в реализации Execute. Наконец, в классе MacroCommand должны быть операции для добавления и удаления подкоманд:
void MacroCommand::Add (Command* с) { _cmds->Append(c); }
void MacroCommand::Remove (Command* c) { _cmds->Remove(c); }
|