前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式 -- 命令模式

设计模式 -- 命令模式

作者头像
xy_ss
发布2023-11-22 09:28:41
1470
发布2023-11-22 09:28:41
举报
文章被收录于专栏:浮躁的喧嚣浮躁的喧嚣

场景

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

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

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

表述 (行为型模式)

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

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

命令模式类图

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

//需求
//研发
//上传
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-08-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景
  • 表述 (行为型模式)
  • 优点
  • 缺点
  • 使用场景
  • 示例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档