Menu
CoddyTech

命令模式

本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 56 课,共 70 课。

命令模式是一种行为型模式,它会将请求转换为一个独立的对象,其中包含有关该请求的所有信息。这样一来,你就可以将请求作为方法参数传递、将其排入队列、记录日志,或支持可撤销的操作。

这种模式将调用操作的对象与知道如何执行操作的对象分离开来。它由四个关键组件组成:命令接口、具体命令、接收者(实际执行工作的对象)和调用者(触发命令的对象)。

public interface ICommand
{
    void Execute();
}

public class Light  // 接收者
{
    public void TurnOn() => Console.WriteLine("Light is ON");
    public void TurnOff() => Console.WriteLine("Light is OFF");
}

public class TurnOnCommand : ICommand
{
    private Light _light;
    
    public TurnOnCommand(Light light) => _light = light;
    
    public void Execute() => _light.TurnOn();
}

public class TurnOffCommand : ICommand
{
    private Light _light;
    
    public TurnOffCommand(Light light) => _light = light;
    
    public void Execute() => _light.TurnOff();
}

调用者存储并执行命令,但不知道这些命令的作用:

public class RemoteControl  // 调用者
{
    private ICommand _command;
    
    public void SetCommand(ICommand command) => _command = command;
    
    public void PressButton() => _command.Execute();
}

// 用法
var light = new Light();
var remote = new RemoteControl();

remote.SetCommand(new TurnOnCommand(light));
remote.PressButton();  // Light 已打开

remote.SetCommand(new TurnOffCommand(light));
remote.PressButton();  // Light 已关闭

命令模式在实现撤销功能、录制宏或将操作排队以便稍后执行时尤其有用。每个命令都封装了执行操作所需的一切,让你的系统更加灵活且易于扩展。

challenge icon

挑战

简单

让我们使用命令模式构建一个文本编辑器命令系统。你将创建一个系统,把插入和删除文本等编辑操作封装为命令对象,让编辑器通过统一的接口执行这些操作。

你将把代码分到 3 个文件中:

  • Command.cs:在 TextEditor 命名空间中定义一个 ICommand 接口,其中只有一个方法 Execute()。然后创建两个具体的命令类:
    • InsertCommand:在构造函数中接收一个 Document 和一个字符串 text。执行时,它会将文本追加到文档中,并输出 Inserted: {text}
    • DeleteCommand:在构造函数中接收一个 Document 和一个 int count。执行时,它会从文档中删除最后 count 个字符,并输出 Deleted: {count} characters
  • Document.cs:在同一个命名空间中创建一个 Document 类。它是接收者,也就是实际执行文本操作的对象。它应包含:
    • 一个用于存储内容的私有字符串字段(初始化为空字符串)
    • 一个将文本添加到内容中的 Append(string text) 方法
    • 一个删除最后 count 个字符的 RemoveLast(int count) 方法(如果 count 超过内容长度,则清空所有内容)
    • 一个返回当前内容的 GetContent() 方法
  • Program.cs:创建一个充当调用者的 Editor 类。它应包含一个 SetCommand(ICommand command) 方法,以及一个用于运行当前命令的 ExecuteCommand() 方法。在主代码中,通过处理一系列编辑操作来演示命令模式。

你将收到以下输入:

  • 要执行的命令数量
  • 对于每条命令:命令类型(insert 或 delete),后面跟着要插入的文本或要删除的字符数

执行完所有命令后,在新的一行中按 Content: {content} 的格式输出最终文档内容。

例如,输入如下:

4
insert
Hello
insert
 World
delete
3
insert
!

输出应为:

Inserted: Hello
Inserted:  World
Deleted: 3 characters
Inserted: !
Content: Hello Wo!

注意,Editor(调用者)不需要了解任何文本操作,它只需执行收到的命令。命令将操作及接收者封装在一起,因此你可以轻松添加 ReplaceCommand 等新操作,而无需修改编辑器!

亲手试试

using System;
using TextEditor;

// TODO:创建 Editor 类(调用者)
public class Editor
{
    // TODO:添加一个私有字段,存储当前命令
    private ICommand _command;

    // TODO:实现 SetCommand 方法
    public void SetCommand(ICommand command)
    {
        // TODO:存储命令
    }

    // TODO:实现 ExecuteCommand 方法
    public void ExecuteCommand()
    {
        // TODO:执行当前命令
    }
}

class Program
{
    public static void Main(string[] args)
    {
        // 读取命令数量
        int n = Convert.ToInt32(Console.ReadLine());

        // 创建文档(接收者)
        Document document = new Document();

        // 创建编辑器(调用者)
        Editor editor = new Editor();

        // TODO:处理每条命令
        for (int i = 0; i < n; i++)
        {
            string commandType = Console.ReadLine();

            // TODO:根据 commandType 创建对应的命令
            // 如果是 "insert":读取文本并创建 InsertCommand
            // 如果是 "delete":读取数量并创建 DeleteCommand
            // 然后在编辑器中设置该命令并执行
        }

        // TODO:按格式 "Content: {content}" 输出最终内容
    }
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: C# 在线编译器