将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。
命令模式可以将请求发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//医生类
class Doctor
{
public:
void TreatEyes()
{
cout << "医生治疗眼病!" << endl;
}
void TreatHead()
{
cout << "医生治疗头疼!" << endl;
}
void TreatNose()
{
cout << "医生治疗鼻子!" << endl;
}
};
//命令抽象类
class AbstractCommand
{
public:
virtual void Execute() = 0;
};
//治疗眼睛指令(命令) 也可理解为病单
class CommandTreatEyes : public AbstractCommand
{
public:
CommandTreatEyes(Doctor* doctor)
{
pDoctor = doctor;
}
virtual void Execute()
{
pDoctor->TreatEyes();
}
private:
Doctor* pDoctor;
};
//治疗头疼命令
class CommandTreatHead : public AbstractCommand
{
public:
CommandTreatHead(Doctor* doctor)
{
pDoctor = doctor;
}
virtual void Execute()
{
pDoctor->TreatHead();
}
private:
Doctor* pDoctor;
};
//治疗鼻子命令
class CommandTreatNose : public AbstractCommand
{
public:
CommandTreatNose(Doctor* doctor)
{
pDoctor = doctor;
}
virtual void Execute()
{
pDoctor->TreatNose();
}
private:
Doctor* pDoctor;
};
//测试
void test01()
{
Doctor* doctor = new Doctor;
//创建病单(命令)
AbstractCommand* commandEyes = new CommandTreatEyes(doctor);
AbstractCommand* commandHead = new CommandTreatHead(doctor);
AbstractCommand* commandNose = new CommandTreatNose(doctor);
commandEyes->Execute();
commandHead->Execute();
commandNose->Execute();
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。
命令模式中的角色和职责
Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。
ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。
Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。
Receiver(接收者):接收者执行与请求相关的操作,它具体实现对请求的业务处理。
命令模式的案例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<list>
using namespace std;
//医生类
class Doctor
{
public:
void TreatEyes()
{
cout << "医生治疗眼病!" << endl;
}
void TreatHead()
{
cout << "医生治疗头疼!" << endl;
}
void TreatNose()
{
cout << "医生治疗鼻子!" << endl;
}
};
//命令抽象类
class AbstractCommand
{
public:
virtual void Execute() = 0;
};
//治疗眼睛指令(命令) 也可理解为病单
class CommandTreatEyes : public AbstractCommand
{
public:
CommandTreatEyes(Doctor* doctor)
{
pDoctor = doctor;
}
virtual void Execute()
{
pDoctor->TreatEyes();
}
private:
Doctor* pDoctor;
};
//治疗头疼命令
class CommandTreatHead : public AbstractCommand
{
public:
CommandTreatHead(Doctor* doctor)
{
pDoctor = doctor;
}
virtual void Execute()
{
pDoctor->TreatHead();
}
private:
Doctor* pDoctor;
};
//治疗鼻子命令
class CommandTreatNose : public AbstractCommand
{
public:
CommandTreatNose(Doctor* doctor)
{
pDoctor = doctor;
}
virtual void Execute()
{
pDoctor->TreatNose();
}
private:
Doctor* pDoctor;
};
//护士类 统一编制病单
class Nurse
{
public:
void addCommand(AbstractCommand* command)
{
m_list.push_back(command);
}
//护士统一提交病单给医生
void submitCommands()
{
for (list<AbstractCommand*>::iterator it = m_list.begin(); it != m_list.end(); it++)
{
(*it)->Execute();
}
}
~Nurse()
{
for (list<AbstractCommand*>::iterator it = m_list.begin(); it != m_list.end(); it++)
{
if (NULL != *it)
{
delete* it;
}
}
}
private:
list<AbstractCommand*> m_list;
};
void test01()
{
Doctor* doctor = new Doctor; //创建医生
Nurse* nurse = new Nurse; //创建护士
AbstractCommand* command = NULL;
command = new CommandTreatEyes(doctor);
nurse->addCommand(command);
command = new CommandTreatHead(doctor);
nurse->addCommand(command);
command = new CommandTreatNose(doctor);
nurse->addCommand(command);
nurse->submitCommands(); //护士统一提交病单给医生
delete nurse;
delete doctor;
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
命令模式的优缺点
优点:
(1)降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。
(2)新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。
(3)可以比较容易地设计一个命令队列或宏命令(组合命令)。
缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。
适用场景
(1) 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心何时被调用。
(2) 系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。