设计模式-命令模式

命令模式是一种设计模式,用于将请求封装为对象,分离请求的发起者和执行者,允许参数化不同请求,支持队列请求、远程执行和撤销操作。在GUI组件、操作队列和事务处理中广泛应用,通过引入抽象接口降低了系统的耦合度,同时也方便了系统的扩展。然而,这也可能导致大量命令类的创建,增加系统复杂性。
摘要由CSDN通过智能技术生成

意图

Encapsulate a request as an object, thereby let you parameterize client with different requests, queue or log requests, and support undoable operations.

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理

现实例子

间谍的秘密文件箱

封装调用

Head first Design pattern在这里插入图片描述

餐馆点餐

在这里插入图片描述
服务员记下顾客的点餐并交给厨师,那张纸就好比命令,它包含了厨师要制作食物的信息,这些点餐单会在厨师那里排队进行制作。餐厅提供的菜单就相当于把请求和处理进行了解耦,这就是命令模式的体现。

遥控器

在这里插入图片描述

看电视时,我们只需要轻轻一按遥控器就能完成频道的切换,这就是命令模式,将换台请求和换台处理完全解耦了。电视机遥控器(命令发送者)通过按钮(具体命令)来遥控电视机(命令接收者)

程序设计例子

按钮菜单

类似于按钮和菜单这类用户前端组件,作为组件的设计者,我们无法预知和实现这些按钮如何被客户使用,也无法知道这些操作会被哪些接受者处理。因此,需要将这些调用和操作封装为一个对象,这些对象可以被存储和传递。这里的设计核心是抽象出Command对象,被封装的命令对象根据完成的命令不同形成具体的命令。
在这里插入图片描述

例如,粘贴命令调用文档的粘贴接口。
在这里插入图片描述
又比如文档打开命令,先调用应用的添加文档接口,然后调用文档对象的open接口打开文档。
在这里插入图片描述

模式结构

在这里插入图片描述
时序图如下:
在这里插入图片描述

在这里插入图片描述

● Command
declares an interface for executing an operation.
● ConcreteCommand (PasteCommand, OpenCommand)
defines a binding between a Receiver object and an action. implements Execute by invoking the corresponding operation(s) on Receiver.
● Client (Application)
creates a ConcreteCommand object and sets its receiver.
● Invoker (MenuItem)
asks the command to carry out the request.
● Receiver (Document, Application)
knows how to perform the operations associated with carrying out a request. Any class may serve as a Receiver

抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

应用场景

  1. parameterize objects by an action to perform, as MenuItem objects did above. You can express such parameterization in a procedural language with a callback function, that is, a function that’s registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks
  2. specify, queue, and execute requests at different times. A Command object can have a lifetime independent of the original request. If the receiver of a request can be represented in an address space- independent way, then you can transfer a command object for the request to a different process and fulfill the request there.
  3. support undo. The Command’s Execute operation can store state for reversing its effects in the command itself. The Command interface must have an added Unexecute operation that reverses the effects of a previous call to Execute. Executed commands are stored in a history list. Unlimited-level undo and redo is achieved by traversing this list backwards and forwards calling Unexecute and Execute, respectively.
  4. support logging changes so that they can be reapplied in case of a system crash. By augmenting the Command interface with load and store operations, you can keep a persistent log of changes. Recovering from a crash involves reloading logged commands from disk and reexecuting them with the Execute operation
  5. structure a system around high-level operations built on primitives operations. Such a structure is common in information systems that support transactions. A transaction encapsulates a set of changes to data. The Command pattern offers a way to model transactions. Commands have a common interface, letting you invoke all transactions the same way. The pattern also makes it easy to extend the system with new transactions

如果你需要通过操作来参数化对象, 可使用命令模式。

命令模式可将特定的方法调用转化为独立对象。 这一改变也带来了许多有趣的应用: 你可以将命令作为方法的参数进行传递、 将命令保存在其他对象中, 或者在运行时切换已连接的命令等。
举个例子: 你正在开发一个 GUI 组件 (例如上下文菜单), 你希望用户能够配置菜单项, 并在点击菜单项时触发操作。

如果你想要将操作放入队列中、 操作的执行或者远程执行操作, 可使用命令模式。

同其他对象一样, 命令也可以实现序列化 (序列化的意思是转化为字符串), 从而能方便地写入文件或数据库中。 一段时间后, 该字符串可被恢复成为最初的命令对象。 因此, 你可以延迟或计划命令的执行。 但其功能远不止如此! 使用同样的方式, 你还可以将命令放入队列、 记录命令或者通过网络发送命令。

如果你想要实现操作回滚功能, 可使用命令模式。

尽管有很多方法可以实现撤销和恢复功能, 但命令模式可能是其中最常用的一种。
为了能够回滚操作, 你需要实现已执行操作的历史记录功能。 命令历史记录是一种包含所有已执行命令对象及其相关程序状态备份的栈结构。
这种方法有两个缺点。 首先, 程序状态的保存功能并不容易实现, 因为部分状态可能是私有的。 你可以使用备忘录模式来在一定程度上解决这个问题。其次, 备份状态可能会占用大量内存。 因此, 有时你需要借助另一种实现方式: 命令无需恢复原始状态, 而是执行反向操作。 反向操作也有代价: 它可能会很难甚至是无法实现。

命令模式的主要优点如下。

  1. 通过引入中间件(抽象接口)降低系统的耦合度。
  2. 扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”。
  3. 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  4. 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
  5. 可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活
  • 单一职责原则。 你可以解耦触发和执行操作的类。
  • 开闭原则。 你可以在不修改已有客户端代码的情况下在程序中创建新的命令。

其缺点是:

  1. 可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性。
  2. 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽离肯定比代码聚合更加难理解。

参考内容

深入设计模式
C语言中文网
HeadFirst DesignPattern
Design Pattern (Gang of Four)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值