目录
- 情景剧场
- 什么是命令模式
- 优缺点
- 优点
- 缺点
- 使用命令模式的步骤
- 命令模式代码示例
- 合理使用AI工具自动生成代码
情景剧场
我们来设想一个场景:
你进入一家餐馆,餐馆只有老板一个人(老板即厨师)。
“老板,一份小炒肉,不要辣。”
老板收到了你的订单请求,去后厨做菜了。这时又进来几位顾客:
“老板,来两份清炒时蔬,不要辣。”
“老板,我要一份清炒时蔬,多放辣。”
“老板,一份小炒肉,多放辣,打包。”
“老板,我的两份清炒时蔬改成一份,打包。”
…
当顾客变多,请求也变多,而每个顾客的请求也非常不一样。老板就很容易分不清谁的需求是什么,做错菜、上错菜都是会发生的错误。顾客对老板直接提出请求,请求多了很容易导致混乱。这里顾客就是请求的发送者,老板就是请求的接收者,请求者和接收者的“紧耦合”虽然逻辑简单,但是很容易僵化。
当给餐馆加上服务员和订单,就会不一样:
顾客走进餐馆开始点单,服务员接过顾客的订单,并将其写在一张纸上。服务员去厨房,把订单贴在墙上,订单按排列顺序到达厨师手中,这样厨师做菜就不会混乱,也不会遗忘。厨师将餐点与订单一起转交服务员,服务员检查订单并将所有东西带到顾客的餐桌上。在此期间,顾客可以撤销订单或增删订单需求。
顾客不需要知道是谁来完成烹饪,厨师不需要知道是谁的订单,纸质订单用作命令,一直排在队列中。这也就实现了请求者和接收者的解耦。
什么是命令模式
命令模式(Command Pattern)是一种行为设计模式,它把请求或操作封装成一个包含有关请求所有信息的独立对象,此转换允许将请求作为方法参数传递,延迟或排队请求的执行,并支持可撤消的操作,从而实现将请求者和接收者解耦。
优缺点
命令模式是一种灵活且功能强大的设计模式,主要优点是它允许在不修改现有客户端代码的情况下添加新的命令。此外,通过使用命令模式,可以将操作记录到日志中、撤销操作或者将操作进行队列化等,同时他也有一些缺点。
优点
-
解耦性:命令模式通过将请求发送者与接收者解耦,使得请求发送者不需要知道接收者的具体实现细节,从而增强了系统的灵活性和可维护性。
-
可扩展性:通过添加新的具体命令类,可以很容易地扩展系统的功能,而不需要修改已有的代码。
-
支持撤销和重做:命令模式可以记录请求的历史操作,从而支持对操作的撤销和重做,提供了更好的用户体验。
-
支持日志和队列:可以将命令对象保存在日志中,实现系统的日志记录功能。此外,命令对象还可以组织成队列,实现命令的批处理和延迟执行。
-
命令模式符合“开-闭”原则。因为将具体命令封装成了一个一个独立的对象,所有当需要修改现有功能时,可以通过添加新代码来实现,而不是修改已有的代码。这样可以提高代码的可维护性和可扩展性,减少出错率和代码复杂度。具体点说,在增加新的具体命令或增加命令的接收者时,不需要修改原有调用者的代码;而在增加新的调用者时,不需要修改原有的具体命令和接收者的代码。
-
命令模式支持宏命令。
即将多个命令组合成一个命令。这样可以将多个操作封装成一个操作,减少了代码的冗余和重复,提高代码的复用率。
缺点
- 类的数量增加:引入命令模式会增加系统中的类的数量,特别是在有大量具体命令类的情况下,可能会导致类的数量激增,增加了系统的复杂性。
- 命令的单一性:每个具体命令类通常只封装了一个特定的操作,这可能会导致系统中存在大量的具体命令类,增加了系统的管理和维护成本。
- 对象间的调用链可能过长:在命令模式中,请求发送者、命令对象、接收者之间可能存在多层的调用链,特别是在复杂的系统中,可能会导致调用链过长,影响系统的性能。
虽然命令模式有一些缺点,但在很多场景下仍然是一种非常有用的设计模式,特别是在需要支持撤销、重做、日志记录和队列等功能的情况下。
使用命令模式的步骤
使用命令模式可以通过以下步骤进行:
- 确定参与者:首先,确定在系统中谁是命令的发起者(Invoker)、命令的接收者(Receiver)、以及具体的命令对象(Command)。
- 定义命令接口:创建一个命令接口,其中包含一个执行操作的方法(如execute())。这个接口可以是抽象类或者接口,具体取决于设计的需求。
- 实现具体命令类:针对每个具体的操作,创建一个具体的命令类,实现命令接口,并在其中封装具体的操作实现。
- 创建接收者类:定义接收者类,实现命令所需的具体操作。 创建调用者类:创建一个调用者类,负责向具体的命令对象发送请求。
- 创建客户端代码:在客户端代码中创建具体的命令对象,并将其关联到调用者对象上。
命令模式代码示例
; 定义命令接口
class ICommand {
Execute() {
; 需要被覆盖的方法(抽象方法)
}
}
; 定义具体命令A
class CommandA extends ICommand {
Execute() {
MsgBox("Command A Executed")
}
}
; 定义具体命令B
class CommandB extends ICommand {
Execute() {
MsgBox("Command B Executed")
}
}
; 定义调用者,负责执行命令
class Invoker {
__New(command) {
this.command := command
}
ExecuteCommand() {
this.command.Execute()
}
}
; 客户端代码
main() {
; 创建命令实例
_commandA := CommandA()
_commandB := CommandB()
; 创建调用者并关联命令
_invokerA := Invoker(_commandA)
_invokerB := Invoker(_commandB)
; 执行命令
_invokerA.ExecuteCommand()
_invokerB.ExecuteCommand()
}
; 运行主程序
main()
合理使用AI工具自动生成代码
上面的代码是用Comate生成的
代码就是上面那部分,我就不截图了,稍微改改就可以运行。也可以把报错信息提示给它,它会给出修改代码示例。
还可以让它输出UML类图:
这个mermaid代码直接在CSDN的波纹MD编辑模式可以直接用哦。
感兴趣可以在VSCODE中安装这个AI编码助手——Comate,感觉能在小众语言AHK上表现这么好还是很令人惊讶的。