1.1 简介
命令模式:将一个请求封装为一个对象,从而使用户可用不同的请求对客户进行参数化。对请求排队或记录请求日志,以及支持撤销的操作。命令模式以松散耦合主题为基础,发送消息、命令和请求,或通过一组处理程序发送任意内容。每个处理程序都会自行判断自己能否处理请求。如果可以,该请求被处理,进程停止。您可以为系统添加或移除处理程序,而不影响其他处理程序。
1.2 命令模式的角色
命令模式的五种角色:-
接收者(Receiver):真正执行命令的对象,负责执行与请求相关的操作
-
命令接口(Command):定义命令的接口,封装execute()、undo()等方法
-
具体命令(ConcreteCommand):命令接口实现对象,实现命令接口中的方法,通常会持有接收者,并调用接收者的功能来完成命令要执行的操作
-
请求者(Invoker):要求命令对象执行请求,通常会持有命令对象,包含Command接口变量
- 客户端(Client):创建具体的命令对象
如下是命令模式的类图:
1.3 命令模式的优缺点
命令模式的优点:-
降低系统的耦合度
-
新的命令可以很容易地加入到系统中
-
可以比较容易地设计一个组合命令
- 调用同一方法实现不同的功能
命令模式的缺点:使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
1.4 具体例子
举个具体的例子,去餐馆吃饭,餐馆存在顾客、服务员、厨师三个角色,作为顾客,只需要列出菜单,传给服务员,服务员通知厨师去实现,而作为服务员,只需要调用准备饭菜这个方法(对厨师通知饭菜),厨师听到要炒菜的请求,立刻去炒。先来实现命令模式的接收者(在这里即厨师): 2_6_Restaurant\Cook.php:1
2/**
3 * 厨师类,实现炒和煮的方法,即命令模式的命令接受者与执行者
4 */
5
6/**
7 * 厨师类,命令接受者与执行者
8 * Class Cook
9 */
10class Cook
11{
12 public function meal()
13 {
14 echo '番茄炒鸡蛋' . PHP_EOL;
15 }
16
17 public function drink()
18 {
19 echo '紫菜蛋花汤' . PHP_EOL;
20 }
21
22 public function ok()
23 {
24 echo '完毕' . PHP_EOL;
25 }
26}
27
然后实现命令接口: 2_6_Restaurant\Command.php: 1
2/**
3 * 命令接口
4 */
5
6/**
7 * 命令接口
8 * Interface Command
9 */
10interface Command
11{
12 public function execute();
13}
接着实现具体的命令(在这里即服务员向厨师通知的具体命令): 2_6_Restaurant\MealCommand.php: 1
2/**
3 * 服务员通知厨师做炒菜的命令类,命令模式中的具体命令之一
4 */
5
6require_once 'Command.php';
7
8/**
9 * 服务员通知厨师做炒菜的命令类
10 * Class MealCommand
11 * @package Cook
12 */
13class MealCommand implements Command
14{
15 private $cook;
16
17 /**
18 * 绑定命令接收者(厨师)
19 * MealCommand constructor.
20 * @param cook $cook
21 */
22 public function __construct(cook $cook)
23 {
24 $this->cook = $cook;
25 }
26
27 /**
28 * 让厨师执行炒菜的命令
29 */
30 public function execute()
31 {
32 $this->cook->meal();
33 }
34}
以及还有煮汤的具体命令:
2_6_Restaurant\DrinkCommand.php:
1
2/**
3 * 服务员通知厨师煮汤的命令类,命令模式中的具体命令之一
4 */
5
6require_once 'Command.php';
7
8/**
9 * 服务员通知厨师煮汤的命令类
10 * Class DrinkCommand
11 * @package Cook
12 */
13class DrinkCommand implements Command
14{
15 private $cook;
16
17 /**
18 * 绑定命令接收者(厨师)
19 * MealCommand constructor.
20 * @param cook $cook
21 */
22 public function __construct(cook $cook)
23 {
24 $this->cook = $cook;
25 }
26
27 /**
28 * 让厨师执行煮汤的命令
29 */
30 public function execute()
31 {
32 $this->cook->drink();
33 }
34}
最后是命令模式的请求者(在这里即顾客点菜): 2_6_Restaurant\CookControl.php:1
2/**
3 * 顾客点菜类,命令模式的请求者
4 */
5
6class CookControl
7{
8 private $mealcommand;
9 private $drinkcommand;
10
11 /**
12 * 将命令发送者绑定到命令接收器上面来
13 * @param Command $mealcommand
14 * @param Command $drinkcommand
15 */
16 public function AddCommand(Command $mealcommand, Command $drinkcommand)
17 {
18 $this->mealcommand = $mealcommand;
19 $this->drinkcommand = $drinkcommand;
20 }
21
22 public function callmeal()
23 {
24 $this->mealcommand->execute();
25 }
26
27 public function calldrink()
28 {
29 $this->drinkcommand->execute();
30 }
31
32}
命令模式的代码已经完成,可以写个代码测试一下,即模拟一下顾客点菜(客户端): 2_6_Restaurant\example.php: 1
2/**
3 * 模拟顾客点了一个菜和一个汤
4 */
5
6require_once 'CookControl.php';
7require_once 'Cook.php';
8require_once 'MealCommand.php';
9require_once 'DrinkCommand.php';
10
11$control = new CookControl();
12$cook = new Cook();
13$mealcommand = new MealCommand($cook);
14$drinkcommand = new DrinkCommand($cook);
15$control->AddCommand($mealcommand, $drinkcommand);
16$control->callmeal();
17$control->calldrink();
18
运行:
1番茄炒鸡蛋
2紫菜蛋花汤