设计模式 -- 命令模式

2023-11-22 09:28:41 浏览数 (1)

场景

在饭店里吃饭经常会出现上餐错误的问题,比如上菜顺序不对或上菜上错桌的情况

问题来了 这种情况在编程中就是常说的紧耦合,客人与厨师之间存在直接关系,当客人要修改菜单时便需要修改厨师的内容,这遍违背了“开闭原则”

问题改进 降低客人和厨师之间的耦合度,客人是点餐的请求者,厨师是烧菜的执行者,在客人和厨师之间需要一个中介服务员,客人不需要认识厨师是谁,饭菜怎么做,客人只需要将点的菜单给服务员就好,他负责去通知厨师,根据不同的订单上不同的菜

表述 (行为型模式)

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作

命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令和执行命令分割开,命令模式允许请求方和接收方独立开来,使得请求方不必知道接收方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的

命令模式类图

  • 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()

//需求
//研发
//上传

0 人点赞