概述

命令模式的结构很简单,但是对于消除代码间的耦合却有着重要的影响。命令模式就是一个函数对象:一个作为对象的函数。通过将函数封装为对象,就能够以参数的形式将其传递给其他函数或者对象,告诉它们在旅行请求的过程中执行特定的操作。

命令模式

根据以上定义,来看一个简单的示例:

/**
* 命令模式,消除代码间的耦合
*/
#include <iostream>
#include <vector>

using namespace std;

class Command
{
public:
virtual void excute() = 0;
virtual ~Command();
};

class Hello : public Command
{
public:
void excute(){cout << "Hello ";}
virtual ~Hello();
};
class World : public Command
{
public:
void excute(){cout << "World! ";}
virtual ~World();
};
class IAm : public Command
{
public:
void excute(){cout << "I'm the command pattern ";}
virtual ~IAm();
};

class Macro
{
public:
void add(Command* c){
commands.push_back(c);
}
void run(){
vector<Command*>::iterator it = commands.begin();
while(it != commands.end()){
(*it++)->excute();
}
}
private:
vector<Command*> commands;
};

int main()
{
Macro macro;
macro.add(new Hello);
macro.add(new World);
macro.add(new IAm);
macro.run();
return 0;
}

命令模式的最主要特点是允许一个函数或者对象传递一个想要的动作,上述示例提供了将一系列想要一起执行的动作集进行排队的方法。

命令模式一个常见的例子就是在应用程序中“撤销操作”功能的实现,没在在用户进行某项操作的时候,相应的“撤销操作”命令对象就被置入一个队列中,而每个命令对象被执行之后,程序的状态就倒退一步。

利用命令模式消除与事件处理的耦合

命令模式可用来消除程序中的耦合问题。每个“常规”的操作必须周期性地调用一个函数来检查事件的状态,而通过命令模式,这些“常规”操作不需要知道有关它们所检查的事件的任何信息,也就是说它们已经与事件处理代码分离开来。

示例:

#include <iostream>
#include <vector>
#include <string>
#include <ctime>
#include <cstdlib>

using namespace std;

class Task
{
public:
virtual void operation() = 0;
};

//单例
class TaskRunner
{
public:
static void add(Task& t){
tasks.push_back(&t);
}
static void run(){
vector<Task*>::iterator it = tasks.begin();
while (it != tasks.end()) {
(*it++)->operation();
}
}

private:
static vector<Task*> tasks;
TaskRunner(){}
static TaskRunner tr;
};


TaskRunner TaskRunner::tr;
vector<Task*> TaskRunner::tasks;

//创建随机延迟时间
//clock() 返回从开启程序进程到调用clock()时的时间。
class EventSimulator
{
public:
EventSimulator():creation(clock()) {
delay = CLOCKS_PER_SEC/4 * (rand() % 20 + 1);
cout << "delay = " << delay << endl;
}

bool fired(){
return clock() > creation + delay;
}

private:
clock_t creation;
clock_t delay;
};

class Button{
public:
Button(string name):pressed(false),id(name){}
void press(){pressed = true;}
bool isPressed(){
if(e.fired()) press();
return pressed;
}

friend ostream& operator <<(ostream & os,const Button& b){
return os << b.id;
}

private:
bool pressed;
string id;
EventSimulator e;
};

class CheckButton : public Task
{
public:
CheckButton(Button & b):button(b),handled(false) {}
void operation(){
if(button.isPressed() && !handled){
cout << button << " pressed" << endl;
handled = true;
}
}

private:
Button & button;
bool handled;
};

void procedure1(){
TaskRunner::run(); // check all events
}
void procedure2(){
TaskRunner::run(); // check all events
}
void procedure3(){
TaskRunner::run(); // check all events
}

int main()
{
srand(time(0));
Button b1("Button 1"),b2("Button 2"),b3("Button 3");
CheckButton cb1(b1),cb2(b2),cb3(b3);
TaskRunner::add(cb1);
TaskRunner::add(cb2);
TaskRunner::add(cb3);
cout<< "Control-C to exit" << endl;

while (true) {
procedure1();
procedure2();
procedure3();
}
return 0;
}

输出为:

delay = 4500000
delay = 3000000
delay = 3000000
Control-C to exit
Button 2 pressed
Button 3 pressed
Button 1 pressed

这个示例中,命令对象由被单例 TaskRunner执行的 Task 表示。EventSimulator创建一个随机延迟时间,所以当周期性的调用函数fired()时,在某个随机时间段,其返回结果从 true 和 false 变化。EventSimulator对象类 Button 中使用,模拟在某个不可预知的时间段用户时间发生的动作。CheckButton 是Task 的实现,在程序中通过所有“正常”代码对其进行周期性检查,就是这里的procedure1(),procedure2(),procedure3()的末尾。

参考资料《C++编程思想》。