命令模式
本课属于 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 已关闭命令模式在实现撤销功能、录制宏或将操作排队以便稍后执行时尤其有用。每个命令都封装了执行操作所需的一切,让你的系统更加灵活且易于扩展。
挑战
简单让我们使用命令模式构建一个文本编辑器命令系统。你将创建一个系统,把插入和删除文本等编辑操作封装为命令对象,让编辑器通过统一的接口执行这些操作。
你将把代码分到 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}" 输出最终内容
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C# 在线编译器