一、命令设计模式概念
命令模式(Command) 是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。
适用场景
- 如果你需要通过操作来参数化对象, 可使用命令模式。
- 如果你想要将操作放入队列中、 操作的执行或者远程执行操作, 可使用命令模式。
- 如果你想要实现操作回滚功能, 可使用命令模式。
命令设计模式的结构:
发送者 (Sender)——亦称 “触发者 (Invoker)”——类负责对请求进行初始化, 其中必须包含一个成员变量来存储对于命令对象的引用。 发送者触发命令, 而不向接收者直接发送请求。 注意, 发送者并不负责创建命令对象: 它通常会通过构造函数从客户端处获得预先生成的命令。
命令 (Command) 接口通常仅声明一个执行命令的方法。
具体命令 (Concrete Commands) 会实现各种类型的请求。 具体命令自身并不完成工作, 而是会将调用委派给一个业务逻辑对象。 但为了简化代码, 这些类可以进行合并。
接收对象执行方法所需的参数可以声明为具体命令的成员变量。 你可以将命令对象设为不可变, 仅允许通过构造函数对这些成员变量进行初始化。
接收者 (Receiver) 类包含部分业务逻辑。 几乎任何对象都可以作为接收者。 绝大部分命令只处理如何将请求传递到接收者的细节, 接收者自己会完成实际的工作。
客户端 (Client) 会创建并配置具体命令对象。 客户端必须将包括接收者实体在内的所有请求参数传递给命令的构造函数。 此后, 生成的命令就可以与一个或多个发送者相关联了。
代码如下:
问题:模拟小餐馆点餐,客户提交订单给服务员,服务员将订单交给厨师,厨师根据订单做菜,最后将菜端给客户。
解决方案:将一个请求封装为一个对象,从而使用户可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
#include <iostream>
#include <string>
#include <vector>
class Kitchen {
private:
Kitchen() {}
public:
static Kitchen* getInstance()
{
static Kitchen* instance=new Kitchen();
return instance;
}
void prepareFood(const std::string& food)
{
std::cout << "厨房:正在准备(" << food << ".)\n";
}
};
//
class Command {
public:
virtual ~Command() {}
virtual void excute()const = 0;
};
//ConcreteCommand
class Order :public Command {
private:
Kitchen* m_kitchen;
std::vector<std::string> m_foods;
public:
Order(Kitchen* kitchen, const std::vector<std::string> foods)
:m_kitchen(kitchen), m_foods(foods)
{
}
virtual ~Order() {}
virtual void excute()const override
{
std::cout << "订单:将订单送往厨房...\n";
for (const auto& food : m_foods)
{
m_kitchen->prepareFood(food);
}
}
};
//Invoker
class Waiter {
private:
Command* m_command;
public:
~Waiter() { delete m_command; }
void orderUp(Command* command)
{
std::cout << "服务员:订单已提交...\n";
m_command = command;//用于回收内存
m_command->excute();
}
};
//Client
class Client {
public:
void orderFood(Waiter& waiter, std::vector<std::string> foods)
{
waiter.orderUp(new Order(Kitchen::getInstance(), foods));
}
};
int main()
{
Client client;
Waiter waiter;
client.orderFood(waiter,{"鱼香肉丝", "宫保鸡丁", "红烧肉"});
return 0;
}
二、与其他模式的关系
- 责任链模式 (opens new window)、 命令模式 (opens new window)、 中介者模式 (opens new window)和观察者模式 (opens new window)用于处理请求发送者和接收者之间的不同连接方式:
- 责任链按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理。
- 命令在发送者和请求者之间建立单向连接。
- 中介者清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通。
- 观察者允许接收者动态地订阅或取消接收请求。
- 责任链 (opens new window)的管理者可使用命令模式 (opens new window)实现。 在这种情况下, 你可以对由请求代表的同一个上下文对象执行许多不同的操作。 还有另外一种实现方式, 那就是请求自身就是一个命令对象。 在这种情况下, 你可以对由一系列不同上下文连接而成的链执行相同的操作。
- 你可以同时使用命令 (opens new window)和备忘录模式 (opens new window)来实现 “撤销”。 在这种情况下, 命令用于对目标对象执行各种不同的操作, 备忘录用来保存一条命令执行前该对象的状态。
- 命令 (opens new window)和策略模式 (opens new window)看上去很像, 因为两者都能通过某些行为来参数化对象。 但是, 它们的意图有非常大的不同。
- 你可以使用命令来将任何操作转换为对象。 操作的参数将成为对象的成员变量。 你可以通过转换来延迟操作的执行、 将操作放入队列、 保存历史命令或者向远程服务发送命令等。
- 另一方面, 策略通常可用于描述完成某件事的不同方式, 让你能够在同一个上下文类中切换算法。
- 原型模式 (opens new window)可用于保存命令 (opens new window)的历史记录。
- 你可以将访问者模式 (opens new window)视为命令模式 (opens new window)的加强版本, 其对象可对不同类的多种对象执行操作。