设计模式---行为变化模式之命令模式(Command)
前提:行为变化模式
在组件的构建过程中,组建行为的变化经常导致组件本身剧烈的变化。“行为变化”模式将组件的行为和组件本身进行解耦,从而支持组件的变化,实现两者之间的松耦合。
类中非虚函数和静态函数方法是以编译时绑定,而虚函数是以虚函数指针指向虚函数表来动态的运行时绑定。
典型模式
命令模式:Command
访问者模式:Visitor
一:Command模式
(一)概念
命令模式是行为设计模式的一种。命令模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数。
在面向对象的程序设计中,一个对象调用另一个对象,一般情况下的调用过程是:创建目标对象实例;设置调用参数;调用目标对象的方法。
但在有些情况下必须要使用一个专门的类对这种调用过程加以封装,我们把这种专门的类称作command类。整个调用过程比较繁杂,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。
调用前后需要对调用参数进行某些处理。调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等。
(二)动机
在软件构建构成中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合——比如需要对行为进行“记录、撤销(undo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。
在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
(三)模式定义
将一个请求(行为)封装为对象<灵活度高>,从而使你可用不同的请求,对客户进行参数化;对请求排队或记录请求日志以及支持可撤销的操作。
——《设计模式》GoF
(四)代码讲解
1.命令基类,定义接口
class Command { public: virtual void execute() = 0; };
2.具体行为命令,继承自Command
class ConcreteCommand1 : public Command { string arg; public: ConcreteCommand1(const string & a) : arg(a) {} void execute() override //处理逻辑 { cout<< "#1 process..."<<arg<<endl; } }; class ConcreteCommand2 : public Command { string arg; public: ConcreteCommand2(const string & a) : arg(a) {} void execute() override { cout<< "#2 process..."<<arg<<endl; } };
3.命令组合
class MacroCommand : public Command { vector<Command*> commands; public: void addCommand(Command *c) { commands.push_back(c); } void execute() override { for (auto &c : commands) //遍历操作 { c->execute(); //动态,运行时绑定 } } };
4.创建命令执行
int main() { ConcreteCommand1 command1(receiver, "Arg ###"); ConcreteCommand2 command2(receiver, "Arg $$$"); MacroCommand macro; macro.addCommand(&command1); macro.addCommand(&command2); macro.execute(); }
command1,command2,macro对象来表征行为,我们可以对其进行序列化等操作,更加灵活
(五)类图(结构)
例如:文本操作,剪切,粘贴,删除,存放在栈中,实现redo,undo操作
(六)要点总结
1.Command模式的根本目的在于“行为请求者”与“行为实现者”解耦,在面向对象的语言中,常见的实现手段是“将行为抽象为对象”
2.实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。通过使用Composite模式,可以将多个“命令”封装为一个“复合命令”MacroCommand。
3.Command模式与C++中的函数对象有些类似。但两者定义行为接口的规范有所区别:Command以面向对象中的“接口-实现”来定义行为接口规范,更严格(必须执行execute),但有性能损失(virtual void execute() = 0;接口规范,继承时必须符合接口规范,性能损失);C++函数对象以函数签名来定义行为接口规范,更灵活,性能能高(参数、返回一致即可,编译时绑定(利用模板编译时多态),性能更高)。
(七)补充(函数对象,重载,重写):
1.在c++中泛型编程、函数对象应用更高;Iterator,Command模式在其他语言应用广泛。
简单例子:
class A { public: int operator() ( int val ) { return val > 0 ? val : -val; } };
int i = -1; A func; cout << func(i);
与普通函数相比,函数对象更加灵活,优势在于:
函数对象可以有自己的状态。我们可以在类中定义状态变量,这样一个函数对象在多次的调用中可以共享这个状态;
函数对象有自己特有的类型。我们可以传递相应的类型作为参数来实例化相应的模板,比如说带参数的函数形参。
类似于其他语言中的闭包函数
2.Overload和Override的区别
Overload:顾名思义,就是Over(重新)——load(加载),所以中文名称是重载。
它可以表现类的多态性,可以是函数里面可以有相同的函数名但是参数名、返回值、类型不能相同;
或者说可以改变参数、类型、返回值但是函数名字依然不变。是一个类中多态性的一种表现。
Override:就是ride(重写)的意思,在子类继承父类的时候子类中可以定义某方法与其父类有相同的名称和参数,
当子类在调用这一函数时自动调用子类的方法,而父类相当于被覆盖(重写)了。是父类与子类之间多态性的一种表现,
override(重写)
1、方法名、参数、返回值相同。 2、子类方法不能缩小父类方法的访问权限。 3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。 4、存在于父类和子类之间。 5、方法被定义为final不能被重写。
overload(重载)
View Code
View Code
1、参数类型、个数、顺序至少有一个不相同。 2、不能重载只有返回值不同的方法名。 3、存在于父类和子类、同类中。
overload是重载,重载是一种参数多态机制,即代码通过参数的类型或个数不同而实现的多态机制。
是一种静态的绑定机制(在编译时已经知道具体执行的是哪个代码段)。
override是覆盖。覆盖是一种动态绑定的多态机制。
即在父类和子类中同名元素(如成员函数)有不同 的实现代码。执行的是哪个代码是根据运行时实际情况而定的。
(八)案例实现<一>反例
小商贩直接卖水果,行为请求者和行为实现者(商贩和命令之间的耦合太强了),若是我们需要对命令进行记录或者其他操作?所以我们有必要使用一个专门的类来封装命令
#include <iostream> using namespace std; class Vendor { public: void sailbanana() { cout << "sale banana" << endl; } void sailapple() { cout << "sale apple" << endl; } }; void main() { Vendor* v = new Vendor(); v->sailapple(); v->sailbanana(); system("pause"); return; }
(九)案例实现<二>使用命令类
小商贩通过命令来实现卖水果,降低和命令耦合,我们可以在命令对象中做其他操作,但是命令对象属于参数,我们不要直接使用命令去执行,所以我们需要一个行为实现者
#include <iostream> using namespace std; class Vendor { public: void sailbanana() { cout << "sale banana" << endl; } void sailapple() { cout << "sale apple" << endl; } }; class Command { public: virtual void sail() = 0; }; class BananaCommand :public Command { private: Vendor* m_v; public: BananaCommand(Vendor* v) { m_v = v; } Vendor* getV() { return m_v; } void setV(Vendor* v) { m_v = v; } virtual void sail() { m_v->sailbanana(); } }; class AppleCommand :public Command { private: Vendor* m_v; public: AppleCommand(Vendor* v) { m_v = v; } Vendor* getV() { return m_v; } void setV(Vendor* v) { m_v = v; } virtual void sail() { m_v->sailapple(); } }; void main() { Vendor* v = new Vendor(); Command * com1 = new AppleCommand(v); Command * com2 = new BananaCommand(v); com1->sail(); com2->sail(); system("pause"); return; }
(十)案例实现<三>增加行为调用者,将命令类作为参数调用
#include <iostream> using namespace std; class Vendor { public: void sailbanana() { cout << "sale banana" << endl; } void sailapple() { cout << "sale apple" << endl; } }; class Command { public: virtual void sail() = 0; }; class BananaCommand :public Command { private: Vendor* m_v; public: BananaCommand(Vendor* v) { m_v = v; } Vendor* getV() { return m_v; } void setV(Vendor* v) { m_v = v; } virtual void sail() { m_v->sailbanana(); } }; class AppleCommand :public Command { private: Vendor* m_v; public: AppleCommand(Vendor* v) { m_v = v; } Vendor* getV() { return m_v; } void setV(Vendor* v) { m_v = v; } virtual void sail() { m_v->sailapple(); } };
class Waiter { private: Command* m_command; public: void sail() { m_command->sail(); } void setCommand(Command* comm) { m_command = comm; } Command* getCommand() { return m_command; } };
void main() { Vendor* v = new Vendor(); Command * com1 = new AppleCommand(v); Command * com2 = new BananaCommand(v); Waiter* w = new Waiter(); w->setCommand(com1); w->sail(); w->setCommand(com2); w->sail(); system("pause"); return; }
(十一)案例实现<四>将命令批量执行
#include <iostream> #include <list> using namespace std; class Vendor { public: void sailbanana() { cout << "sale banana" << endl; } void sailapple() { cout << "sale apple" << endl; } }; class Command { public: virtual void sail() = 0; }; class BananaCommand :public Command { private: Vendor* m_v; public: BananaCommand(Vendor* v) { m_v = v; } Vendor* getV() { return m_v; } void setV(Vendor* v) { m_v = v; } virtual void sail() { m_v->sailbanana(); } }; class AppleCommand :public Command { private: Vendor* m_v; public: AppleCommand(Vendor* v) { m_v = v; } Vendor* getV() { return m_v; } void setV(Vendor* v) { m_v = v; } virtual void sail() { m_v->sailapple(); } };
class Waiter { private: list<Command*>* m_list; public: Waiter() { m_list = new list<Command*>; m_list->resize(0); } void sail() { for (list<Command*>::iterator iter = m_list->begin(); iter != m_list->end(); iter++) (*iter)->sail(); } void setCommands(Command* comm) { (*m_list).push_back(comm); } list<Command*>* getCommand() { return m_list; } };
void main() { Vendor* v = new Vendor(); Command * com1 = new AppleCommand(v); Command * com2 = new BananaCommand(v); Waiter* w = new Waiter(); w->setCommands(com1); w->setCommands(com2); w->sail(); system("pause"); return; }
(十二)案例实现:医生,护士,病人简历
#include <iostream> #include <list> #include <string> using namespace std; class Doctor { public: void threatEyes() { cout << "doctor threating eyes" << endl; } void threatNose() { cout << "doctor threating nose" << endl; } }; class Resume { protected: string p_name; int p_age; Doctor* doc; public: virtual void threat() = 0; virtual ~Resume(){} }; class ResumeNose :public Resume { public: ResumeNose(Doctor* d, string name, int age) { doc = d; p_name = name; p_age = age; } virtual void threat() { cout << "patient:" << p_name << "\tage:" << p_age << endl; doc->threatNose(); } }; class ResumeEyes:public Resume { public: ResumeEyes(Doctor* d, string name, int age) { doc = d; p_name = name; p_age = age; } virtual void threat() { cout << "patient:" << p_name << "\tage:" << p_age << endl; doc->threatNose(); } }; class Nurse { private: list<Resume*>* r_list; public: Nurse() { r_list = new list<Resume*>; r_list->resize(0); } void AddResume(Resume* r) { r_list->push_back(r); } void threat() { for (list<Resume*>::iterator iter = r_list->begin(); iter != r_list->end(); iter++) (*iter)->threat(); } list<Resume*>* getResumes() { return r_list; } }; void main() { Doctor* doc = new Doctor(); ResumeEyes* re = new ResumeEyes(doc, "zhangsan", 15); ResumeNose* rn = new ResumeNose(doc, "lisi", 35); Nurse* nur = new Nurse(); nur->AddResume(re); nur->AddResume(rn); nur->threat(); delete nur; delete re; delete rn; delete doc; system("pause"); return; }