命令模式
一、概述
顾名思义,命令模式其实和现实生活中直接下命令 的动作类似,怎么理解这个命令是理解命令模式的关键!!!直接说结论是很不负责的行为,因此我将会结合之后的例子来向你介绍它,来帮助你更好的理解,而不是仅仅死记硬背它。这样你会在以后需要的时候想起它并且通过这个命令模式帮助你解决问题。
二、经典举例
-
遥控器
现在你手里有一个遥控器,上面有很多的按钮,可以对应到生活中很对例子,如:电视遥控器、空调遥控器等等,使用起来非常的简单,我们只需要按下对应功能的按钮就可以,不需要知道怎么做的,比如说开关空调,调整温度等等。在这个场景中,按下按钮其实就会给设备发送一个命令,请先记住这个场景下的命令,后面会用到!!
-
点餐
你到餐厅点餐,服务员会将菜单递给你,然后你会挑选菜单上的菜,然后服务就会记下来交给厨师,厨师收到后就会开始准备这些菜。在这个过程中,也是存在命令的,可选择的命令来自于菜单,然后你下达的命令会由服务员记录在小本本上然后传达给做菜的厨师。
-
小结
说完上面两个场景,现在我们来总结一下,这两个场景中的命令有什么共性。在你继续往下查看结果之前,我建议你先自己思考一下,然后再去下面的结果。
有一天,你突然心血来潮想通过遥控器控制空调放音乐,这可能就没办法做到了,为什么呢?因为你手中的遥控器上就没有这个命令呀。同样的道理,你有一天去平日里最爱的餐厅里想点一道满汉全席,服务员看了直摇头,他不知所措了,为啥呢,因为菜单上没有这道菜。
通过上面的假设,我想你应该知道了这个命令的特点了:
这些 "命令" 都是提前预设好的,因此数量也是有限的,且无法做到每个命令都很灵活
命令其实就是这样也应该这样,它不是一个模糊的东西,它的含义非常明确且简单。
当然,你如果有其他的理解,也希望你能够在评论区不吝分享。
三、代码示例(Go)
示例代码主要是围绕命令所做的抽象,命令的方法应该尽可能的减少入参或者没有入参。下面我们就遥控器的场景来书写示例代码:
go
package main
import "fmt"
// Command 命令对像
type Command interface {
Execute()
}
// TurnOnLightCommand 开灯命令
type TurnOnLightCommand struct {
Light *Light
}
// Execute 执行命令
func (c TurnOnLightCommand) Execute() {
c.Light.On()
}
// TurnOffLightCommand 关灯命令
type TurnOffLightCommand struct {
Light *Light
}
func (c TurnOffLightCommand) Execute() {
c.Light.Off()
}
// TurnOnFanCommand 开风扇命令
type TurnOnFanCommand struct {
Fan *Fan
}
func (c TurnOnFanCommand) Execute() {
c.Fan.On()
}
// TurnOffFanCommand 关风扇命令
type TurnOffFanCommand struct {
Fan *Fan
}
func (c TurnOffFanCommand) Execute() {
c.Fan.Off()
}
// ConcreteCommand 宏命令,可以执行多个命令
type ConcreteCommand struct {
Commands []Command
}
func (c ConcreteCommand) Execute() {
for _, command := range c.Commands {
command.Execute()
}
}
// Light 灯
type Light struct {
Name string
}
func (l Light) On() {
fmt.Println(l.Name + " on")
}
func (l Light) Off() {
fmt.Println(l.Name + " off")
}
// Fan 风扇
type Fan struct {
Name string
}
func (f Fan) On() {
fmt.Println(f.Name + " on")
}
func (f Fan) Off() {
fmt.Println(f.Name + " off")
}
// SimpleRemoteControl 简单的遥控器,只有一个按钮
type SimpleRemoteControl struct {
Commands Command
}
// ButtonWasPressed 按钮被按下
func (rc SimpleRemoteControl) ButtonWasPressed() {
rc.Commands.Execute()
}
// NormalRemoteControl 普通的遥控器,有多个按钮
type NormalRemoteControl struct {
Commands []Command
}
// ButtonWasPressed 按钮被按下
func (rc NormalRemoteControl) ButtonWasPressed(index int) {
rc.Commands[index].Execute()
}
func main() {
// 简单的遥控器
fmt.Println("简单的遥控器")
light := Light{Name: "Living Room"}
remoteControl := SimpleRemoteControl{Commands: TurnOnLightCommand{Light: &light}}
remoteControl.ButtonWasPressed()
// 普通的遥控器
fmt.Println("普通的遥控器")
fan := Fan{Name: "Living Room"}
normalRemoteControl := NormalRemoteControl{Commands: []Command{TurnOnLightCommand{Light: &light}, TurnOffLightCommand{Light: &light}, TurnOnFanCommand{Fan: &fan}, TurnOffFanCommand{Fan: &fan}}}
normalRemoteControl.ButtonWasPressed(0)
normalRemoteControl.ButtonWasPressed(1)
normalRemoteControl.ButtonWasPressed(2)
normalRemoteControl.ButtonWasPressed(3)
// 通过宏,让一个按钮可以执行多个命令
fmt.Println("通过宏,让一个按钮可以执行多个命令")
normalRemoteControl.Commands = []Command{ConcreteCommand{Commands: []Command{TurnOnLightCommand{Light: &light}, TurnOnFanCommand{Fan: &fan}}}, ConcreteCommand{Commands: []Command{TurnOffLightCommand{Light: &light}, TurnOffFanCommand{Fan: &fan}}}}
normalRemoteControl.ButtonWasPressed(0)
}
-
分析
代码中最核心的是抽象了
Command
,通过这个接口的Execute()
方法不关心命令究竟是如何执行的,毕竟它仅仅只是命令而已。 有了这个接口,我们就可以在此基础之上拓展很多新的应用场景出来,就比如示例中的
宏命令
------由几个命令组合而成,除此之外,还有可以有很多其他拓展,希望你们能够自己亲自去实现,这样能够提升自己的理解。可拓展的应用场景:
-
撤销/回滚功能
这个很好理解,其实就是在
Command
接口中增加一个Undo()
方法,然后再把执行过的命令放入栈中,进行命令回滚时,只需要执行出栈命令的Undo()
方法即可。 -
远程执行
既然命令已经被抽象成了对象,那么也就和对象一样可以被序列化(变成可传输的字符串)然后传输到远端去或者持久化到数据库中。
-
其他
-
四、总结
其实命令模式并不难,最最最核心的正入它的名字一样,是对业务 "命令" 的抽象,因此也有些地方把这个设计模式成为 调用封装
。
有了上面的理解后我们来体会一下此模式的定义,相信你会有更深刻的理解:
命令模式将 "请求" 封装成对象,以便使用不同的请求、队列或日志来参数化其他对象。命令模式支持可撤销的操作
以上,便是此文章的全部内容了,希望你能有收获,如果内容存在错误的地方,也欢迎指出