什么是指令行形式

指令形式是一种数据驱动的行为设计形式,首要恳求以指令的形式包裹在目标中并传递给调用目标,然后调用目标能够查找处理该指令的适宜目标,并且将该指令传递给相应的目标,最后由该目标履行指令。指令行形式能够将恳求或许简略的操作转换为目标,此类转换能够让开发者能够延迟履行或许长途履行恳求,还能够将其放入行列中。

2023 跟我一起学设计模式:命令行模式

指令形式结构

  1. 调用者 (Invoker)调用者需求有个参数来保存指令类目标。 发送者履行指令, 而不是向接收者直接发送需求履行的指令恳求。 但是发送者并不负责创立指令目标: 它通常会经过结构函数从客户端处获得预先生成的指令。
  2. 指令 : 指令接口通常只声明一个履行指令的办法。
  3. 详细指令 :完结各种类型的恳求的类。 详细指令本身并不履行任务, 而是会将调用委派给一个事务逻辑目标。 但为了简化代码,能够将详细指令兼并。
  4. 接收者 :包括部分事务逻辑的接口和类。 大部分目标都能够作为接收者。 绝大部分指令只处理将恳求传递到接收者, 接收者会完结详细的作业。
  5. 客户端 : 创立并配置详细指令目标。 客户端必须将包括接收者目标在内的一切恳求参数传递给指令的初始化函数。 尔后生成的指令就能够与一个或多个调用者相相关了。

指令行形式运用场景

  • 假如你需求经过操作参数化目标可运用指令形式。

指令形式可将特定的办法调用转化为独立目标的调用。 这种形式给代码带来了很好的灵活性: 咱们将指令类作为办法的参数进行传递给接受者,将指令类保存在其他目标中, 或许在程序运行时切换已连接好的指令等操作。

例如: 你正在开发一个前端界面组件 (比方后台办理界面的左侧菜单树), 你希望用户能够配置菜单项, 并且在用户点击菜单项时触发对应的操作动作指令。

  • 假如开发者要将操作放入行列中或许履行操作,可运用指令形式。

同其他目标相同, 能够将指令完结序列化 (转化为字符串), 然后能方便地写入文件或数据库中存储。 在需求用到的时分, 该字符串可被康复成为开始的指令目标。 因此开发者能够延迟履行指令或许方案什么时分履行指令。 但其功用远不止如此! 运用相同的办法, 开发者还能够将指令放入行列、记载指令或许经过网络发送指令给其他体系调用。

  • 假如开发者需求完结操作回滚的功用, 能够运用指令行形式。

为了能够回滚操作, 开发者需求完结已履行操作的历史记载功用。 指令历史记载功用是一种包括一切已履行指令目标及其相关程序状况备份的栈结构。这种办法有两个缺陷,一个是程序状况的坚持功用比较难完结, 因为部分状况可能是私有的。 开发者能够运用备忘录形式来在一定程度上处理这个问题。 其次, 备份状况可能会占用很多物理机内存。 因此, 咱们有时需求凭借另一种完结办法: 指令无需康复原始状况, 而是履行反向操作。 反向操作也有价值: 它可能会很难甚至是无法完结。

指令行形式的完结办法

  1. 界说仅有一个履行办法的指令接口。

  2. 界说详细指令类以及其办法,抽取恳求并使其成为完结指令接口的详细指令类。每个详细的指令类需求有一组成员变量,用于坚持其恳求参数,和实践接受者目标的使用,这些变量都需求经过指令进行初始化。

  3. 界说调用者类及其办法。找到担任调用者责任的类,在这些类中添加存储指令的成员变量。调用者只能经过指令接口与其指令进行交互,调用者通常并不创立指令目标,他会经过客户端来获取指令目标。

  4. 界说接受者类机器办法。

  5. 创立客户端,客户端必须按照以下次序来初始化目标:

    • 创立接收者目标。
    • 创立指令, 假如有需求能够将其相关至接收者目标类。
    • 创立发送者类并将其与特定指令类相关起来。

指令形式长处

  • 指令行形式能够减低代码的耦合度,并且将恳求调用者与恳求接受者进行了解耦
  • 指令行形式的扩展性高。在指令行形式中假如要扩展新的指令,那么直接界说新的指令即可, 假如要履行一组指令,那么给接受者发送一组指令即可

指令形式缺陷

  • 指令行形式会添加复杂度。扩展指令会添加类数量的很多添加,然后添加了体系完结的复杂度。
  • 指令形式需求针对每个指令都开发一个与之对应的指令类,然后添加了较多的代码量

代码示例

下面咱们经过收音机的例子来加深对指令形式的了解。 在咱们的生活中,你能够经过以下办法办法来翻开收音机:

  • 按下遥控器上的 ON 开关;
  • 按下收音机上的 ON 开关。

首要完结ON指令目标,并且将收音机作为接受者; 当在此指令上调用 execute履行办法时,该办法会调用 Radio.On翻开收音机函数,用于翻开收音机。最后界说恳求者的作业: 遥控器和收音机。 二者都嵌入 ON 指令目标。

创立独立指令目标的优势在于能够将操作逻辑与底层事务逻辑解耦,这样就能够不用为每个恳求者开发不同的处理这了。指令目标中包括了履行操作所需求的全部信息,所以能够延迟履行操作。详细的操作如下:

界说调用者类: Button 以及其办法 Press()

button.go: 恳求者

go
仿制代码
package main
type Button struct {
	command Command
}
func (b *Button) press() {
	b.command.execute()
}

界说仅有一个办法的接口 Command

command.go: 指令接口

go
仿制代码
package main
type Command interface {
	execute()
}

界说详细指令OnCommand 以及其办法 Execute()

onCommand.go:

go
仿制代码
package main
type OnCommand struct {
	device Device
}
func (c *OnCommand) execute() {
	c.device.On()
}

offCommand.go: 详细接口

go
仿制代码
package main
type OffCommand struct {
	device Device
}
func (c *OffCommand) execute() {
	c.device.Off()
}

界说接受者接口 Device

device.go: 接收者接口

go
仿制代码
package main
type Device interface {
	On()
	Off()
}

界说接受者类Radio 以及其对应的办法 On(), Off()

tv.go: 详细接收者

go
仿制代码
package main
import "fmt"
type Radio struct {
	isRunning bool
}
func (r *Radio) On() {
	r.isRunning = true
	fmt.Println("Turning Radio on")
}
func (r *Radio) Off() {
	r.isRunning = false
	fmt.Println("Turning Radio off")
}

main.go: 客户端代码

go
仿制代码
package main
func main() {
	radio := &Radio{}
	onCommand := &OnCommand{
		device: radio,
	}
	offCommand := &OffCommand{device: radio}
	onButton := &Button{
		command: onCommand,
	}
	onButton.press()
	offButton := &Button{
		command: offCommand,
	}
	offButton.press()
}