Command Pattern
-
Hallo.
Ich habe mal ein wenig im Internet gestöbert und das Command pattern gefunden. Es heisst, man kann damit sowas wie Events und Undo-/Redo Operationen programmieren. Allerdings verstehe ich das Prinzip dieses Patterns nicht. Ich steige durch die Schaubilder, die dieses verdeutlichen sollen, einfach nicht durch.
Kann mir jemand erklären, wie das funktioniert?
-
ich habe für dich ein kleines Beispiel geschrieben, das zeigt, wie man Events, Undo-/Redo Operationen programmieren kann
#include <stack> #include <string> #include <iostream> //abstract command iterface struct ICommand { //execute action virtual void Do() = 0; //undo action virtual void Undo() = 0; virtual ~ICommand(){} }; //command processor class CommandProcessor { CommandProcessor() { } public: //singleton von Mayers static CommandProcessor &instance() { static CommandProcessor proc; return proc; } //process command void processCommand(ICommand *cmd) { cmd->Do(); history.push(cmd); } void redo() { //ich denke, das machst du schon selber } //undo last command void undo() { if(!history.empty()) { std::auto_ptr<ICommand> cmd(history.top()); cmd->Undo(); history.pop(); } } //can make undo operation bool canUndo() const { return !history.empty(); } ~CommandProcessor() { //clear command history while(!history.empty()) { std::auto_ptr<ICommand> cmd(history.top()); history.pop(); } } public: //history of commands std::stack<ICommand*> history; }; class Person { public: void setName(const std::string &name) { this->name = name; } const std::string &getName() const { return name; } private: std::string name; }; //command to change a name of person class ChangePersonName : public ICommand { public: ChangePersonName(Person &person, const std::string &newName) : person(person), name(newName) { } private: std::string name; Person &person; private: //ICommand virtual void Do() { //save old name std::string oldName = person.getName(); //set new name person.setName(name); //swap name = oldName; } virtual void Undo() { //restore old name person.setName(name); } }; //persons Person p1, p2, p3; //display names void display() { std::cout << p1.getName() << " " << p2.getName() << " " << p3.getName() << "\n"; } int main(int argc, char** argv) { //get command processor CommandProcessor &proc = CommandProcessor::instance(); std::cout << "init persons\n"; //diese drei Operationen gehen nich in historystack p1.setName("Name1"); p2.setName("Name2"); p3.setName("Name3"); display(); std::cout << "\nchange first name\n"; proc.processCommand(new ChangePersonName(p1, "Name1_modified")); display(); std::cout << "\nchange second name\n"; proc.processCommand(new ChangePersonName(p2, "Name2_modified")); display(); std::cout << "\nundo previos change\n"; proc.undo(); display(); std::cout << "\nchange third name\n"; proc.processCommand(new ChangePersonName(p3, "!!!!")); display(); std::cout << "\nundo all\n" ; while(proc.canUndo()) { proc.undo(); } display(); return 0; }
-
Hmmm,
du hast das jetzt mit einem Singleton implementiert. Ist das die einzige Möglichkeit? Ich kann mich erinnern, dass in den Tutorials, die ich so gefunden habe, oftmals Callbacks und Funktionspointer benutzt wurden.
Gruß Sebastian.
-
ich habe den Singleton nur für CommandProcessor benutzt. ob es für dich richtig oder falsch ist, musstest du selber entscheiden.
Singleton selbst(genauso wie eine andere Implementirung) hat aber nichts mit dem Pattern "Command" zu tun.
Man kann Callbacks oder Funktionspointer für diese Sache auch benutzen. ich packte das ganze lieber in eine Klasse (ChangePersonName)
-
ssm schrieb:
ich packte das ganze lieber in eine Klasse (ChangePersonName)
Was bei Command durchaus praktisch ist, weil Funktionen ja keinen State haben und man des oefteren etwas fuer das "undo" speichern muss.
-
@ssm
Danke für den Code.
Ist ein netter Denkansatz!