命令模式 - Command Pattern
定义
将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
设计的原则和思想
- 解耦的是行为请求(发起)者和行为实现(执行)者。
- 不变部分是行为实现者,变化部分是行为请求者。
- 核心思想是将函数封装成命令对象。
一句话概括设计模式
调用者通过命令类让接受者执行指令。
结构中包含的角色
- Command(抽象命令类)
- ConcreteCommand(具体命令类)
- Invoker(调用者,行为请求者,动作的发起者)
- Receiver(接收者,行为实现者,动作的承受者)
最小可表达代码
// 接收者
class Receiver
{
public function action()
{
echo '执行命令的具体逻辑';
}
}
// 抽象命令类
interface Command
{
public function execute();
}
// 具体命令类
class ConcreteCommand implements Command
{
private $receiver;
public function __construct()
{
$this->receiver = new Receiver();
}
public function execute()
{
$this->receiver->action();
}
}
// 调用者
class Invoker
{
private $command;
public function __construct(Command $command)
{
$this->command = $command;
}
public function call()
{
$this->command->execute();
}
}
$invoker = new Invoker(new ConcreteCommand());
$invoker->call();
优点
- 在不修改代码的情况下创建新的命令。
- 实现操作的延迟执行。
- 命令可以相互组合,组合一个复杂命令。
- 一个命令对象和请求的初始调用者可以有不同的生命期。
- 可以控制命令的执行流程。异步、延迟、排队执行命令、撤销重做命令、存储命令等。
缺点
- 代码会变得更加复杂。
- 命令模式是为了松耦合。如果调用者或者具体命令类增多,维护性都会降低。
何时使用
- 需要将请求调用者和请求接收者解耦。
- 某些请求需要延迟执行。可以将请求写入队列,然后延迟执行队列。
- 将特定的方法调用转化为对象。
实际应用场景
- 操作回滚功能。备忘录模式可能会占用大量内存。命令模式就是反向操作。
- 实现队列。将命令类序列化放入队列中,然后执行。
- 电脑开机。开机键就是一个命令。
- 饭店点菜。每点的一道菜都是一个命令。