Menu
CoddyTech

命令模式

本课属于 Coddy Dart 学习旅程的“面向对象编程”单元。第 94 课,共 110 课。

命令模式将请求封装为对象,让你可以用不同的请求对客户端进行参数化、将操作加入队列,或支持撤销功能。你不再直接调用方法,而是将操作包装在命令对象中,以便存储、传递或稍后执行。

该模式包含三个关键组件:带有 execute 方法的 Command 接口、实现特定操作的具体命令,以及执行实际工作的接收者。你还可以选择使用调用者来触发命令,而无需了解命令的具体作用:

abstract class Command {
  void execute();
}

class Light {
  void turnOn() => print('Light is ON');
  void turnOff() => print('Light is OFF');
}

class TurnOnCommand implements Command {
  final Light light;
  TurnOnCommand(this.light);

  @override
  void execute() => light.turnOn();
}

class TurnOffCommand implements Command {
  final Light light;
  TurnOffCommand(this.light);

  @override
  void execute() => light.turnOff();
}

class RemoteControl {
  Command? _command;

  void setCommand(Command command) {
    _command = command;
  }

  void pressButton() {
    _command?.execute();
  }
}

void main() {
  var light = Light();
  var remote = RemoteControl();

  remote.setCommand(TurnOnCommand(light));
  remote.pressButton();  // 灯已打开

  remote.setCommand(TurnOffCommand(light));
  remote.pressButton();  // 灯已关闭
}

RemoteControl 对灯一无所知,它只会执行收到的命令。这种解耦让你可以轻松添加新命令,而无需修改调用者。命令模式非常适合实现撤销/重做系统、任务队列或宏录制,因为在这些场景中,你需要将操作视为一等对象。

challenge icon

挑战

简单

让我们使用命令模式构建一个文本编辑器命令系统!你将创建一个系统,把文本操作封装为命令对象,让编辑器无需了解每种操作的具体细节,就能执行不同的操作。这是命令模式的经典应用场景,你可以把它想象成一个撤销/重做系统,其中每个操作都存储为一个对象。

你将把代码分成两个文件:

  • commands.dart:在这里创建命令系统。先创建一个抽象类 Command,并声明一个 execute() 方法。然后创建一个 TextDocument 类(接收者),它包含一个 String content 字段(初始为空),并有三个方法:
    • append(String text):在内容末尾添加文本
    • clear():将内容设为空字符串
    • show():打印 Document: [content]
    接下来,实现三个操作 TextDocument 的具体命令:
    • AppendCommand:构造函数接收文档和文本,执行时添加文本
    • ClearCommand:构造函数接收文档,执行时清空文档
    • ShowCommand:构造函数接收文档,执行时显示文档内容
    最后,创建一个 TextEditor 类(调用者),用于存储命令列表。它应该有一个 addCommand(Command command) 方法,用于将命令加入队列;还应该有一个 runAll() 方法,按顺序执行队列中的所有命令。
  • main.dart:导入命令文件,并演示命令模式。创建一个 TextDocument 和一个 TextEditor。按顺序将以下命令加入队列:添加 Hello,添加 World,显示文档,添加 !,再次显示,清空文档,最后再显示一次。然后运行队列中的所有命令。

注意,TextEditor 不知道添加、清空或显示的具体操作,它只负责执行交给它的命令。这种解耦正是命令模式的核心!

预期输出:

Document: Hello World
Document: Hello World!
Document: 

亲手试试

import 'commands.dart';

void main() {
  // TODO:创建一个 TextDocument 实例

  // TODO:创建一个 TextEditor 实例

  // TODO:按顺序将以下命令加入队列:
  // 1. 追加 "Hello"
  // 2. 追加 " World"
  // 3. 显示文档
  // 4. 追加 "!"
  // 5. 再次显示文档
  // 6. 清空文档
  // 7. 最后显示一次文档

  // TODO:运行所有已加入队列的命令
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: Dart 在线编译器