前提:行为变化模式

在组件的构建过程中,组建行为的变化经常导致组件本身剧烈的变化。“行为变化”模式将组件的行为和组件本身进行解耦,从而支持组件的变化,实现两者之间的松耦合。
类中非虚函数和静态函数方法是以编译时绑定,而虚函数是以虚函数指针指向虚函数表来动态的运行时绑定。

典型模式

命令模式:Command
访问者模式:Visitor

一:Command模式

(一)概念

命令模式是行为设计模式的一种。命令模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数。
在面向对象的程序设计中,一个对象调用另一个对象,一般情况下的调用过程是:创建目标对象实例;设置调用参数;调用目标对象的方法。

但在有些情况下必须要使用一个专门的类对这种调用过程加以封装,我们把这种专门的类称作command类。整个调用过程比较繁杂,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。

调用前后需要对调用参数进行某些处理。调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等。

(二)动机

在软件构建构成中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合——比如需要对行为进行“记录、撤销(undo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。
在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。 

(三)模式定义

将一个请求(行为)封装为对象<灵活度高>,从而使你可用不同的请求,对客户进行参数化;对请求排队或记录请求日志以及支持可撤销的操作。

                                                                           ——《设计模式》GoF

(四)代码讲解

1.命令基类,定义接口

class Command
{
public:
virtual void execute() = ;
};

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 > ? val : -val;
}
};
int i = -;
A func;
cout << func(i);

与普通函数相比,函数对象更加灵活,优势在于:

函数对象可以有自己的状态。我们可以在类中定义状态变量,这样一个函数对象在多次的调用中可以共享这个状态;
函数对象有自己特有的类型。我们可以传递相应的类型作为参数来实例化相应的模板,比如说带参数的函数形参。
类似于其他语言中的闭包函数

2.Overload和Override的区别

Overload:顾名思义,就是Over(重新)——load(加载),所以中文名称是重载。
它可以表现类的多态性,可以是函数里面可以有相同的函数名但是参数名、返回值、类型不能相同;
或者说可以改变参数、类型、返回值但是函数名字依然不变。是一个类中多态性的一种表现。 Override:就是ride(重写)的意思,在子类继承父类的时候子类中可以定义某方法与其父类有相同的名称和参数,
当子类在调用这一函数时自动调用子类的方法,而父类相当于被覆盖(重写)了。是父类与子类之间多态性的一种表现,
override(重写) 

、方法名、参数、返回值相同。
、子类方法不能缩小父类方法的访问权限。
、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
、存在于父类和子类之间。
、方法被定义为final不能被重写。
overload(重载)

、参数类型、个数、顺序至少有一个不相同。
、不能重载只有返回值不同的方法名。
、存在于父类和子类、同类中。
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() = ;
}; 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() = ;
}; 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();
} 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() = ;
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();
} 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", );
ResumeNose* rn = new ResumeNose(doc, "lisi", ); Nurse* nur = new Nurse();
nur->AddResume(re);
nur->AddResume(rn); nur->threat(); delete nur;
delete re;
delete rn;
delete doc; system("pause");
return;
}

设计模式---行为变化模式之命令模式(Command)的更多相关文章

  1. IOS设计模式之四(备忘录模式,命令模式)

    本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq) ...

  2. C#设计模式学习笔记:(14)命令模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7873322.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第二个模式--命 ...

  3. Java进阶篇设计模式之八 ----- 责任链模式和命令模式

    前言 在上一篇中我们学习了结构型模式的享元模式和代理模式.本篇则来学习下行为型模式的两个模式, 责任链模式(Chain of Responsibility Pattern)和命令模式(Command ...

  4. Java设计模式之八 ----- 责任链模式和命令模式

    前言 在上一篇中我们学习了结构型模式的享元模式和代理模式.本篇则来学习下行为型模式的两个模式, 责任链模式(Chain of Responsibility Pattern)和命令模式(Command ...

  5. 设计模式之第14章-命令模式(Java实现)

    设计模式之第14章-命令模式(Java实现) “小明,滚出去.”“小明,这个问题怎么做?”(可怜的小明无奈躺枪.小明:老师,我和你有什么仇什么怨,我和你有什么仇什么怨啊到底...老师:小明,滚出去.习 ...

  6. IDEA terminal无法从vim的编辑模式转换为命令模式

    Git 修改最后一次的commit历史记录:https://www.baidu.com/link?url=2WF8yFd0iBuVmXLWfutmSoXa12K9D143e_B0A3PTYYHEP9r ...

  7. .NET设计模式(17):命令模式(Command Pattern)(转)

    概述 在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”.但在某些场合,比如要对行为进行“记录.撤销/重做.事务”等处理,这种无法抵御变化的紧耦合是不合适的.在这种情况下,如何将“行为 ...

  8. 【设计模式】行为型06命令模式(Command Pattern)

    命令模式 个人理解:命令模式,本质上是一种多层次的封装. 好处:降低耦合,扩展极其方便. 以下一段文案摘抄自:http://www.runoob.com/design-pattern/command- ...

  9. 设计模式----行为型模式之命令模式(Command Pattern)

    下面来自head first设计模式的命令模式一章节. 定义 将"请求"封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作. 类图 注: 1. ...

随机推荐

  1. mfs 使用心得

    CentOS的安装方法: To install MooseFS from officially supported repository on EL7, follow the steps below: ...

  2. Codeforces Round #449 Div. 1

    B:注意到nc/2<=m,于是以c/2为界决定数放在左边还是右边,保证序列满足性质的前提下替换掉一个数使得其更靠近边界即可. #include<iostream> #include& ...

  3. HDU5773-The All-purpose Zero-多校#41010-最长上升子序列问题

    只想到了朴素的n^2做法,然后发现可以用splay维护.于是调了几个小时的splay... splay的元素是从第二个开始的!第一个是之前插入的头节点! #include <cstdio> ...

  4. LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】

    题目分析: 好题.本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法. 首先考虑静态的情况.这个的DP方程非常容易写出来. 接着可以注意到对于异或结果的计数可以看成一个FW ...

  5. Matplotlib学习---用matplotlib画直方图/密度图(histogram, density plot)

    直方图用于展示数据的分布情况,x轴是一个连续变量,y轴是该变量的频次. 下面利用Nathan Yau所著的<鲜活的数据:数据可视化指南>一书中的数据,学习画图. 数据地址:http://d ...

  6. Android studio preview界面无法预览,报错render problem

    1.查看报错信息,如果有报错,该叹号应为红色,点击查看报错,显示为render problem 2.打开res/styles.xml修改为如图,添加Base. 3.再打开preview界面

  7. Android 错误提示: Can't create handler inside thread that has not called Looper.prepare()

    Can't create handler inside thread that has not called Looper.prepare() 将 Handler handler = new Hand ...

  8. rdesktop ERROR: CredSSP: Initialize failed, do you have correct kerberos tgt initialized ? Failed to connect, CredSSP required by server

    错误信息: ERROR: CredSSP: Initialize failed, do you have correct kerberos tgt initialized ? Failed to co ...

  9. Up-to-date cache with EclipseLink and Oracle

    Up-to-date cache with EclipseLink and Oracle One of the most useful feature provided by ORM librarie ...

  10. cf 990G - GCD Counting

    题意 #include<bits/stdc++.h> #define t 200000 #define MAXN 200100 using namespace std; int n; in ...