Menu
CoddyTech

命令模式

本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 75 课,共 91 课。

命令模式是一种行为型设计模式,它会将请求转换为一个独立的对象,其中包含有关该请求的所有信息。通过这种转换,你可以将请求作为方法参数传递,延迟执行请求或将请求加入队列,并支持可撤销的操作。

想象一家餐厅。

你点餐时,服务员会把你的点单写在一张单子上(也就是命令),然后把它送到厨房,由厨师执行。服务员不需要知道怎么做菜,只要传递命令就行。

这样就将请求者与执行者解耦了。

这是一个基本实现:

<?php
interface Command {
    public function execute(): string;
}
class Light {
    public function on(): string { return "Light is ON"; }
    public function off(): string { return "Light is OFF"; }
}
class LightOnCommand implements Command {
    public function __construct(private Light $light) {}
    
    public function execute(): string {
        return $this->light->on();
    }
}
class LightOffCommand implements Command {
    public function __construct(private Light $light) {}
    
    public function execute(): string {
        return $this->light->off();
    }
}

调用者会触发命令,但不知道这些命令会做什么:

<?php
class RemoteControl {
    public function press(Command $command): string {
        return $command->execute();
    }
}
$light = new Light();
$remote = new RemoteControl();

echo $remote->press(new LightOnCommand($light)) . "\n";
echo $remote->press(new LightOffCommand($light));

输出:

Light is ON
Light is OFF

当你需要将操作加入队列、实现撤销功能或记录操作时,命令模式就很有用。每个命令都是独立的,因此无需修改现有代码就能轻松添加新命令。

challenge icon

挑战

简单

让我们使用命令模式构建一个文本编辑器命令系统。文本编辑器通常需要支持写入文本、删除文本和撤销操作等功能:每个操作都会成为一个独立的命令对象,并且可以通过统一的接口执行。

你需要将代码组织在 4 个文件中:

  • Command.php:定义一个 Command 接口,其中包含一个方法 execute(): string。这规定了所有编辑器命令都必须遵循的契约。
  • TextEditor.php,创建一个 TextEditor 类来表示接收者,也就是实际执行操作的对象。编辑器应当:
    • 存储当前文本内容(初始值为空字符串)
    • 包含一个 write(string $text): string 方法,该方法会将文本追加到内容中,并返回 "Written: [text]"
    • 包含一个 clear(): string 方法,该方法会清空内容并返回 "Content cleared"
    • 包含一个 getContent(): string 方法,该方法会返回当前内容
  • Commands.php:引入 Command 和 TextEditor 文件。创建两个实现 Command 接口的命令类:
    • WriteCommand:在构造函数中接收一个 TextEditor 和一个文本字符串。它的 execute() 方法会调用编辑器的 write() 方法并返回结果。
    • ClearCommand:在构造函数中接收一个 TextEditor。它的 execute() 方法会调用编辑器的 clear() 方法并返回结果。
  • main.php:引入 Commands 文件。你会收到两个输入:命令类型("write" 或 "clear")和一个文本字符串(仅用于写入命令)。

    创建一个 TextEditor 实例。根据命令类型,创建相应的命令对象。然后创建一个调用者函数或简单机制,对该命令调用 execute() 并打印结果。

    在新的一行中,按以下格式打印编辑器当前的内容:Content: [content](如果内容为空,只打印 Content:,空格后不添加任何内容)。

这个挑战展示了命令模式如何将每个操作封装为一个对象。调用者不需要知道它执行的是写入命令还是清空命令,只需调用 execute(),由命令处理具体细节即可。

亲手试试

<?php

require_once 'Commands.php';

// 读取输入
$commandType = trim(fgets(STDIN)); // "write" 或 "clear"
$text = trim(fgets(STDIN));         // write 命令的文本

// TODO:创建一个 TextEditor 实例

// TODO:根据命令类型,创建相应的命令对象
// 如果是 "write",用编辑器和文本创建 WriteCommand
// 如果是 "clear",用编辑器创建 ClearCommand

// TODO:创建一个调用机制,对命令调用 execute()
// 并打印结果

// TODO:按以下格式打印编辑器的当前内容:"Content: [content]"
// (如果内容为空,只打印 "Content: ",空格后不打印任何内容)

?>
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: PHP 在线编译器