前提:行为变化模式

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

典型模式

命令模式: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. jquery 循环绑定click的问题

    之前循环数据,通过live绑定click, 发觉每个click绑定的链接参数都是一样的. 后来改用 直接的 click绑定,就好了. $.each(ship.PPRList, function (i, ...

  2. 洛谷P1118数字三角形题解

    题目 这个题我们乍一看会有些熟悉.觉得是可以用DP来做的那个题.但是打眼一看,就会发现不对了.因为那个题是顺推而这个题则是逆推. 这样的话可怎么办呢. 我们可以在草稿纸上推一下,我们随便写个数n. 再 ...

  3. 【BZOJ3999】【TJOI2015】旅游 树剖

    题目大意 给你一棵树,有\(n\)个点.有\(q\)个操作,每次要你从\(x\)到\(y\)的路径上选两个点,使得距离\(x\)比较远的点的点权\(-\)距离\(x\)比较近的点的点权最大,然后把这条 ...

  4. 七牛云 qshell 使用

    七牛云 qshell 控制台工具上传 命令:qshell fput another1 demo.txt /users/tianyang/demo.txt ======================= ...

  5. PHP linux ZendGuardLoader.so: undefined symbol: executor_globals

    /usr/xxx/php    xxx/xxx.php 报了这个错. 本人出现此问题的原因:  php执行程序路径错了. 解决: linux下执行   which php   命令  查看php真实路 ...

  6. scrapy简单使用

    #settings.py文件设置 #如果网站中没有robots文件,就不会抓取任何数据 ROBOTSTXT_OBEY = False #设置请求头 DEFAULT_REQUEST_HEADERS = ...

  7. 【BZOJ3691】游行(网络流)

    [BZOJ3691]游行(网络流) 题面 BZOJ 然而权限题. Description 每年春季,在某岛屿上都会举行游行活动. 在这个岛屿上有N个城市,M条连接着城市的有向道路. 你要安排英雄们的巡 ...

  8. JavaScript面向对象--记录防忘记(一)

    1.理解对象: 创建自定义对象最简单的方式就是创建一个object实例,再给他添加属性和方法. let person = new Object(); person.name = 'zhangsan'; ...

  9. A1076. Forwards on Weibo

    Weibo is known as the Chinese version of Twitter. One user on Weibo may have many followers, and may ...

  10. Python之迭代器,生成器

    迭代器 1.什么是可迭代对象 字符串.列表.元组.字典.集合都可以被for循环,说明他们都是可迭代的. from collections import Iterable l = [1,2,3,4] t ...