场景
在饭店里吃饭经常会出现上餐错误的问题,比如上菜顺序不对或上菜上错桌的情况
问题来了 这种情况在编程中就是常说的紧耦合,客人与厨师之间存在直接关系,当客人要修改菜单时便需要修改厨师的内容,这遍违背了“开闭原则”
问题改进 降低客人和厨师之间的耦合度,客人是点餐的请求者,厨师是烧菜的执行者,在客人和厨师之间需要一个中介服务员,客人不需要认识厨师是谁,饭菜怎么做,客人只需要将点的菜单给服务员就好,他负责去通知厨师,根据不同的订单上不同的菜
表述 (行为型模式)
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令和执行命令分割开,命令模式允许请求方和接收方独立开来,使得请求方不必知道接收方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的
命令模式类图
- Command(抽象命令类):一般是一个抽象类或接口,在其中声明了execute()方法用于执行请求
- ConcreteCommand(具体命令类):抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,在实现execute()方法时,将调用接收者对象的相关操作(Action)
- Invoker(调用者):请求发送者,它通过命令对象来执行请求。调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象传入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作
- Receiver(接收者):具体实现对请求的业务处理
优点
- 降低耦合度
- 比较容易设计一个命令队列
- 需要的情况下,可以较为容易的将命令记入日志
- 可以容易地实现对请求的撤销和重做
- 由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易
缺点
使用命令模式可能会导致某些系统有过多的具体命令类
使用场景
- 想让程序支持撤销与恢复
- 想用对象参数化一个动作已执行操作,并用不同命令对象来代替回调函数
- 想要在不同时刻对请求进行指定、排列和执行
- 想记录修改日志,这样系统故障时,这些修改可在后来重做一遍
示例
- 需求1:命令模式
- 需求2:命令模式--迭代版
- 需求3:命令模式--命令队列
- 需求4:命令模式--撤销操作
- 需求5:命令模式--请求日志
- 需求6:命令模式--宏命令
需求V1:客人在饭店点了热菜
抽象命令类
代码语言:javascript复制class Command {
func execute() {
}
}
具体命令类
代码语言:javascript复制class HotFoodCommand : Command{
var hotCook : HotCook
override init() {
self.hotCook = HotCook.init()
}
override func execute() {
self.hotCook.makeHotFood()
}
}
发布者
代码语言:javascript复制class Waiter {
var command:Command
init(command:Command) {
self.command = command
}
func notify() {
self.command.execute()
}
}
接收者
代码语言:javascript复制class HotCook {
func makeHotFood() {
print("热菜")
}
}
客户端
代码语言:javascript复制//具体命令
let hotFoodCom = HotFoodCommand.init()
//发布者
let waiter = Waiter.init(command: hotFoodCom)
waiter.notify()
//log:
//热菜
需求V2:客人又加了凉菜 增加一个新的具体命令类ColdFoodCommand和对应的接收者类ColdCook即可
代码语言:javascript复制class ColdFoodCommand : Command {
var coldCook : ColdCook
override init() {
self.coldCook = ColdCook.init()
}
override func execute() {
self.coldCook.makeColdFood()
}
}
class ColdCook {
func makeColdFood() {
print("凉菜")
}
}
客户端
代码语言:javascript复制let coldFoodCom = ColdFoodCommand.init()
let waiter = Waiter.init(command: coldFoodCom)
waiter.notify()
//log
//凉菜
需求V3:客人在饭店点了热菜和凉菜(命令队列的实现)
有时需要将多个请求排队,当一个发送者发送请求后,将有一系列接收者对请求作出响应,这些请求接收者将逐个执行业务方法,完成对请求的处理。此时,我们可以通过命令队列来实现,如果请求接收者的接收次序没有严格的先后次序,我们还可以使用多线程技术来并发调用命令对象的execute()方法,从而提高程序的执行效率。
命令队列的实现方法有多种形式,其中最常用、灵活性最好的一种方式是增加一个CommandQueue类,由该类来负责存储多个命令对象,而不同的命令对象可以对应不同的请求接收者
增加一个CommandQueue类
代码语言:javascript复制class CommandQueue {
var commands = [Command]()
func addCommand(command:Command) {
commands.append(command)
}
func remove(command:Command) {
for i in 0..<commands.count {
if commands[i] === command {
commands.remove(at:i )
}
}
}
func execute() {
for command in commands {
command.execute()
}
}
}
在增加了命令队列类CommandQueue以后,请求发送者类Invoker将针对CommandQueue编程
代码语言:javascript复制class Waiter {
var commandQueue:CommandQueue
init(commandQueue:CommandQueue) {
self.commandQueue = commandQueue
}
func notify() {
self.commandQueue.execute()
}
}
客户端
代码语言:javascript复制let hotFoodCom = HotFoodCommand.init()
let coldFoodCom = ColdFoodCommand.init()
let queue = CommandQueue.init()
queue.addCommand(command: hotFoodCom)
queue.addCommand(command: coldFoodCom)
let waiter = Waiter.init(commandQueue: queue)
waiter.notify()
//log:
//热菜
//凉菜
需求V4:一个简易计算器,该计算器可以实现简单的数学运算,还可以对运算实施撤销操作(撤销操作的实现)
抽象命令类
代码语言:javascript复制class Command {
func execute(value:Int) -> Int {
return 0
}
func undo() -> Int {
return 0
}
}
具体命令类
代码语言:javascript复制class AddCommand : Command {
var adder = Adder.init()
var value = 0
var commands = [Int]()
override func execute(value: Int) -> Int {
self.value = value
commands.append(value)
return adder.add(value: value)
}
override func undo() -> Int {
var value = 0
for _ in 0..<commands.count {
value = commands[commands.count - 1]
commands.remove(at: commands.count-1)
return adder.add(value: -value)
}
return 0
}
}
接收者
代码语言:javascript复制class Adder {
var num = 0
func add(value:Int) -> Int {
num = value
return num
}
}
发布者
代码语言:javascript复制class CalculatorForm {
var command : Command
init(command:Command) {
self.command = command
}
func compute(value:Int) {
let i = self.command.execute(value: value)
print("执行运算,结果为:(i)")
}
func undo() {
let i = self.command.undo()
print("执行撤销,结果为:(i)")
}
}
客户端
代码语言:javascript复制let commandd = AddCommand.init()
let form = CalculatorForm.init(command: commandd)
form.compute(value: 1)
form.compute(value: 1)
form.compute(value: 1)
form.compute(value: 1)
form.compute(value: 1)
print("----")
form.undo()
form.undo()
//执行运算,结果为:1
//执行运算,结果为:2
//执行运算,结果为:3
//执行运算,结果为:4
//执行运算,结果为:5
// ----
//执行撤销,结果为:4
//执行撤销,结果为:3
需求5:数据库支持增删改查的功能,在此基础上添加日志(请求日志)
抽象命令类
代码语言:javascript复制class Command {
func execute(args:String) {
}
func execute() {
}
}
具体命令类
代码语言:javascript复制class InsertCommand : Command {
var oper : Operator
var name : String
var args : String?
init(name:String) {
self.name = name
self.oper = Operator.init()
}
override func execute(args: String) {
self.args = args
self.oper.insert(args: args)
}
override func execute() {
self.oper.insert(args: self.args!)
}
}
class UpdataCommand : Command {
var oper : Operator
var name : String
var args : String?
init(name:String) {
self.name = name
self.oper = Operator.init()
}
override func execute(args: String) {
self.args = args
self.oper.updata(args: args)
}
override func execute() {
self.oper.updata(args: self.args!)
}
}
class DeleteCommand : Command {
var oper : Operator
var name : String
var args : String?
init(name:String) {
self.name = name
self.oper = Operator.init()
}
override func execute(args: String) {
self.args = args
self.oper.delete(args: args)
}
override func execute() {
self.oper.delete(args: self.args!)
}
}
接收者
代码语言:javascript复制class Operator {
func insert(args:String) {
print("新增数据 : (args)")
}
func updata(args:String) {
print("修改数据 : (args)")
}
func delete(args:String) {
print("删除数据 : (args)")
}
}
发布者
代码语言:javascript复制class ExecuteTool {
var commands = [Command]()
var command : Command?
func setCommand(command:Command) {
self.command = command
}
func call(args:String) {
self.command!.execute(args: args)
self.commands.append(self.command!)
}
func save() {
print("保存数据")
}
func recover() {
print("恢复数据")
for obj in commands {
obj.execute()
}
}
}
客户端
代码语言:javascript复制var execute = ExecuteTool.init()
let insertComA = InsertCommand.init(name: "增加")
execute.setCommand(command: insertComA)
execute.call(args: "insert A")
let insertComB = InsertCommand.init(name: "增加")
execute.setCommand(command: insertComB)
execute.call(args: "insert B")
let insertComC = InsertCommand.init(name: "增加")
execute.setCommand(command: insertComC)
execute.call(args: "insert C")
print("---")
let updataB = UpdataCommand.init(name: "更新")
execute.setCommand(command: updataB)
execute.call(args: "updata B")
print("---")
let deleteC = DeleteCommand.init(name: "删除")
execute.setCommand(command: deleteC)
execute.call(args: "delete C")
print("---")
execute.save()
print("---")
execute.recover()
//新增数据 : insert A
//新增数据 : insert B
//新增数据 : insert C
//---
//修改数据 : updata B
//---
//删除数据 : delete C
//---
//保存数据
//---
//恢复数据
//新增数据 : insert A
//新增数据 : insert B
//新增数据 : insert C
//修改数据 : updata B
//删除数据 : delete C
需求6:一个APP上线简要概括为,需求、研发、上线(宏命令)
宏命令又称为组合命令,是组合模式和命令模式联用产物
宏命令是一个具体命令类,它拥有一个集合属性,该集合中包含了对其他命令的引用,通常宏命令不直接与请求接受者交互,而是通过它的成员来调用接受者的方法,当调用宏命令的execute()方法时,将递归调用他所包含的每个成员命令的execute()方法
抽象命令类
代码语言:javascript复制class Command {
func execute() {
}
}
宏命令类
代码语言:javascript复制class MacroCommand : Command {
func addCommand(command:Command) {
}
func removeCommand(command:Command) {
}
}
class APPMacroCommand : MacroCommand {
var commands = [Command]()
override func addCommand(command: Command) {
commands.append(command)
}
override func removeCommand(command: Command) {
for i in 0..<commands.count {
if commands[i] === command {
commands.remove(at: i)
break
}
}
}
override func execute() {
for com in commands {
com.execute()
}
}
}
具体命令类
代码语言:javascript复制class DemandCommand : Command {
var develop : Developer
override init() {
self.develop = Developer.init()
}
override func execute() {
self.develop.demand()
}
}
class DevelopmentCommand : Command {
var develop : Developer
override init() {
self.develop = Developer.init()
}
override func execute() {
self.develop.development()
}
}
class UploadCommand : Command {
var develop : Developer
override init() {
self.develop = Developer.init()
}
override func execute() {
self.develop.upload()
}
}
接收者
代码语言:javascript复制class Developer {
func demand() {
print("需求")
}
func development() {
print("研发")
}
func upload() {
print("上传")
}
}
客户端
代码语言:javascript复制let app = APPMacroCommand.init()
let demandCommand = DemandCommand.init()
app.addCommand(command: demandCommand)
let developmentCommand = DevelopmentCommand.init()
app.addCommand(command: developmentCommand)
let uploadCommand = UploadCommand.init()
app.addCommand(command: uploadCommand)
app.execute()
//需求
//研发
//上传