持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情
本文主要介绍iOS设计模式中的命令模式,命令模式主要把请求对象封装成一个命令进行传递,由命令者和接受者组成。
1. 什么是命令模式
战场上,将军把封在信封里的定时指令交给部下,然后他们到时候打开信封并执行其中的指令。同样的指令既可以是一次性的,也可以是被不同人在指定时间在此执行的,因为命令封在信封里,所以为了各种目的在不同地区之间传递起来会比其他方式(比如电话或其他通信线路上的口头指令)更为容易。
又比如在市中心逛了很久的街后, 你找到了一家不错的餐厅, 坐在了临窗的座位上。 一名友善的服务员走近你, 迅速记下你点的食物, 写在一张纸上。 服务员来到厨房, 把订单贴在墙上。 过了一段时间, 厨师拿到了订单, 他根据订单来准备食物。 厨师将做好的食物和订单一起放在托盘上。 服务员看到托盘后对订单进行检查, 确保所有食物都是你要的, 然后将食物放到了你的桌上。
那张纸就是一个命令, 它在厨师开始烹饪前一直位于队列中。 命令中包含与烹饪这些食物相关的所有信息。 厨师能够根据它马上开始烹饪, 而无需跑来直接和你确认订单详情。
面向对象的设计中,借用了类似的思想,把指令封装在各种命令对象中。命令对象可以被传递并且在指定时刻被不同的客户端复用。
命令模式:将请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
2.什么时候使用命令模式
下列情况,可以考试使用命令模式
- 想让应用程序
支持撤销与恢复。 - 想用对象
参数化一个动作以执行操作,并用不同命令对象来代替回调函数。 - 想要在
不同时刻请求进行指定,排列和执行。 - 想记录
修改日志,这样在系统故障时,这些修改可在后来重做一遍。 - 想让系统支持事务,事务封装了对数据的一系列修改。事务可以建模为
命令对象。
3. 代码展示
import XCTest
/// The Command interface declares a method for executing a command.
protocol Command {
func execute()
}
/// Some commands can implement simple operations on their own.
class SimpleCommand: Command {
private var payload: String
init(_ payload: String) {
self.payload = payload
}
func execute() {
print("SimpleCommand: See, I can do simple things like printing (" + payload + ")")
}
}
/// However, some commands can delegate more complex operations to other
/// objects, called "receivers."
class ComplexCommand: Command {
private var receiver: Receiver
/// Context data, required for launching the receiver's methods.
private var a: String
private var b: String
/// Complex commands can accept one or several receiver objects along with
/// any context data via the constructor.
init(_ receiver: Receiver, _ a: String, _ b: String) {
self.receiver = receiver
self.a = a
self.b = b
}
/// Commands can delegate to any methods of a receiver.
func execute() {
print("ComplexCommand: Complex stuff should be done by a receiver object.\n")
receiver.doSomething(a)
receiver.doSomethingElse(b)
}
}
/// The Receiver classes contain some important business logic. They know how to
/// perform all kinds of operations, associated with carrying out a request. In
/// fact, any class may serve as a Receiver.
class Receiver {
func doSomething(_ a: String) {
print("Receiver: Working on (" + a + ")\n")
}
func doSomethingElse(_ b: String) {
print("Receiver: Also working on (" + b + ")\n")
}
}
/// The Invoker is associated with one or several commands. It sends a request
/// to the command.
class Invoker {
private var onStart: Command?
private var onFinish: Command?
/// Initialize commands.
func setOnStart(_ command: Command) {
onStart = command
}
func setOnFinish(_ command: Command) {
onFinish = command
}
/// The Invoker does not depend on concrete command or receiver classes. The
/// Invoker passes a request to a receiver indirectly, by executing a
/// command.
func doSomethingImportant() {
print("Invoker: Does anybody want something done before I begin?")
onStart?.execute()
print("Invoker: ...doing something really important...")
print("Invoker: Does anybody want something done after I finish?")
onFinish?.execute()
}
}
/// Let's see how it all comes together.
class CommandConceptual: XCTestCase {
func test() {
/// The client code can parameterize an invoker with any commands.
let invoker = Invoker()
invoker.setOnStart(SimpleCommand("Say Hi!"))
let receiver = Receiver()
invoker.setOnFinish(ComplexCommand(receiver, "Send email", "Save report"))
invoker.doSomethingImportant()
}
}
执行结果
Invoker: Does anybody want something done before I begin?
SimpleCommand: See, I can do simple things like printing (Say Hi!)
Invoker: ...doing something really important...
Invoker: Does anybody want something done after I finish?
ComplexCommand: Complex stuff should be done by a receiver object.
Receiver: Working on (Send email)
Receiver: Also working on (Save report)
4.小结
命令模式大部分情况下, 它被用于代替包含行为的参数化 UI 元素的回调函数比如我们的button, 此外还被用于对任务进行排序和记录操作历史记录等。通过命令模式可以消除应用程序中命令,调用器,接收器,和客户端的耦合,如果特定的命令需要在实现中做修改,那么其他组件中大部分都不受影响。而且添加新的命令类非常容易,因为不必为此修改已有的类。