美文网首页设计模式
9、HeadFirst--命令模式(Swift)

9、HeadFirst--命令模式(Swift)

作者: barry | 来源:发表于2019-03-19 17:08 被阅读6次

    1、概述

    命令模式:将请求封装成对象,以便使用不同的请求、日志、队列等来参数化其他对象。命令模式也支持撤销操作。

    demo地址,欢迎star

    2、需求

    假设某个公司需要设计一个多用功能的遥控器。基本的需求如下:
    该遥控器有可以控制风扇,白炽灯,热水器等等的多对开关,而且可能还有其他的电器,暂时不做其功能,但是希望可以保留接口,用的时间可以方便的扩展。
    除上面的需求之外,还需要有个按钮,可以撤销上一步的操作

    3、问题

    在设计遥控器时,风扇,白炽灯,热水器的开关方法已经定义好,其名字各不相同。不妨设置其方法为如下:
    由于各种电器的开关方法都不一样,而且还存在一个待扩展的电器,如果没有学习命名模式之前,我们在设置扩展的开关时,会出现的问题是什么呢?假设现在有电视,冰箱还可能会用到遥控器,那么我们会在最后一个开关上写if else,当然如果哪一天有多了一个大门也加入了我们的遥控的行列,这样我们继续加if else ,很显然随着电器的高速发展,会有多个需要遥控可以控制的。

    举个例子,如果我们是需要遥控的客户,现在有一款遥控如果有遥控可以进行扩展,一种是可以扩展指定类型的,像上面的,只能再去扩展电视和冰箱中的一种,偶尔有一天你看到隔壁邻居的门,也可以使用遥控了,所以你去把你的高级遥控器,拿到扩展店时,扩展工程师说了,现在只能扩展电视和冰箱,不支持对大门的遥控扩展.

    我们肯定是希望,可以自由的扩展,大门可以使用遥控了,就对大门扩展,车门使用遥控了,就对车门扩展……其实也就是一种松耦合的实现。

    3.1、分析问题

    为了实现松耦合,我们现在来想一下,周末去请朋友吃饭,服务员mm问你吃什么,你说水煮活鱼,然后在菜单上面,写上水煮活鱼。下个星期天想吃花生米啤酒,同样也写在订单上,然后服务员mm把订单拿给厨师。

    在上面的例子中,无论你点了什么菜,服务员mm,只需要知道顾客点的什么菜,从而给不同的厨师做(虽然不是直接的,但最终凉菜会给凉菜的师傅做,热菜的会给热菜的厨师做),然而具体的怎么做是厨师的事情,服务员知道顾客点上面菜,只是为了能正确的交个厨师去做。

    其实在这个例子中,也是一个命令模式的例子,不同的订单对应的有不同的厨师,最终订单拿到厨师面前,就是对厨师下了个命令,要做菜了。每订单或者说是每种菜都对应着自己的厨师,所以,客服需要有订单的的引用,订单为了知道调用哪个厨师,需要有厨师引用;两个引用都是使用的组合。从而可以调用多种自己需要对象的方法来实现松耦合。这里记住:客服mm知道顾客点菜的目的是为了让不同的厨师去做。再直接一些就是为了使用不同的命令。

    上面的吃饭问题和我们的遥控器问题差不多,都是包含下命令,命令的执行者,以及命令的具体内容。如果还是有些不清楚的话,就用简单的程序模拟一下上面的过程:

    上面的例子中,厨师的做法有可能不同,就像我们遥控器的开关一样,电器的开关方法不一定相同。如果要执行,只需要把这个方法包在命令的激发方法中,然后去调用具体的方法就可以了。

    3.2、解决问题

    回到我们的遥控器问题上面来,我们可以先定义好我们的风扇,白炽灯,热水器。然后定义其分别的开关命令,每个命令都有自己对应的电器引用,而且会在命令的Excute中包装电器的开或者关,最后需要把命令安装到遥控器上面,在遥控器上每个按钮都对应有自己的激发方法
    这样基本上就实现了我们的现有的三种电器的遥控。需要注意的是,在开始初始化遥控器时,对每个命令初始化成了NoCommand,也就是什么都不执行。在命令的初始化经常使用,同时这也解决了我们的在扩展前什么都不做的难题。看了风扇,白炽灯,热水器的遥控实现,进一步的扩展任何的电器,相信都不是什么难事。但是还有个功能没有实现,就是撤销到上一步的操作,接下来我们就来实现撤销操作。

    4、命令模式UML

    命令模式.jpg

    demo地址,欢迎star

    5、代码实现(Swift)

    Command协议

    protocol CommandProtocol {
        func excute()
    }
    

    开灯命令(其它命令类似)

    class LightOffCommand: CommandProtocol {
        var light: Light
    
        init(light: Light) {
            self.light = light
        }
    
        func excute() {
            light.off()
        }
    }
    
    class LightOffCommand: CommandProtocol {
        var light: Light
    
        init(light: Light) {
            self.light = light
        }
    
        func excute() {
            light.off()
        }
    }
    

    灯对象

    • 每一个动作对应一个命令
    class Light {
        var title: String
    
        init(title: String) {
            self.title = title
        }
    
        func on() {
            print("Light is on")
        }
    
        func off() {
            print("Light is off")
        }
    }
    

    Invoker对象

    • 对应上述的服务员
    class RemoteControl {
        var onCommands: [CommandProtocol]
        var offCommands: [CommandProtocol]
    
        init() {
            onCommands = []
            offCommands = []
            let noCommand = NoCommand()
            (0..<7).forEach { _ in
                onCommands.append(noCommand)
                offCommands.append(noCommand)
            }
        }
    
        func setCommandAt(_ index: Int, onCommand: CommandProtocol, offCommand: CommandProtocol) {
            onCommands[index] = onCommand
            offCommands[index] = offCommand
        }
    
        func onButtonDidPressedAt(_ index: Int) {
            onCommands[index].excute()
        }
    
        func offButtonDidPressed(_ index: Int) {
            offCommands[index].excute()
        }
    }
    

    Client

    let light = Light(title: "Living Room")
    let kitchenLight = Light(title: "Kitchen")
    let ceilingFan = CeilingFan(title: "Living Room")
    let garageDoor = GarageDoor(title: "GarageDoor")
    let stereo = Stereo(title: "Living Room")
    
    let remoteControl = RemoteControl()
    
    //普通灯
    let lightOnCommand = LightOnCommand(light: light)
    let lightOffCommand = LightOffCommand(light: light)
    //厨房灯
    let kitchenLightOnCommand = LightOnCommand(light: light)
    let kitchenLightOffCommand = LightOffCommand(light: light)
    //吊扇
    let ceilingFanOnCommand = CeilingFanOnCommand(ceilingFan: ceilingFan)
    let ceilingFanOffCommand = CeilingFanOffCommand(ceilingFan: ceilingFan)
    //车库
    let garageDoorUpCommand = GarageDoorUpCommand(garageDoor: garageDoor)
    let garageDoorDownCommand = GarageDoorDownCommand(garageDoor: garageDoor)
    //音响
    let stereoOnCommand = StereoOnCommand(stereo: stereo)
    let stereoOffCommand = StereoOffCommand(stereo: stereo)
    
    
    //设置命令
    remoteControl.setCommandAt(0, onCommand: lightOnCommand, offCommand: lightOffCommand)
    remoteControl.setCommandAt(1, onCommand: kitchenLightOnCommand, offCommand: kitchenLightOffCommand)
    remoteControl.setCommandAt(2, onCommand: ceilingFanOnCommand, offCommand: ceilingFanOffCommand)
    remoteControl.setCommandAt(3, onCommand: garageDoorUpCommand, offCommand: garageDoorDownCommand)
    
    //执行
    remoteControl.onButtonDidPressedAt(0)
    remoteControl.offButtonDidPressed(0)
    remoteControl.onButtonDidPressedAt(1)
    remoteControl.offButtonDidPressed(1)
    remoteControl.onButtonDidPressedAt(2)
    remoteControl.offButtonDidPressed(2)
    remoteControl.onButtonDidPressedAt(3)
    remoteControl.offButtonDidPressed(3)
    remoteControl.onButtonDidPressedAt(6)
    remoteControl.offButtonDidPressed(6)
    

    6、总结

    命令模式主要通过中介Command实现了发出命令者和命令的执行者,也即Invoke类和Receiver的松耦合。本文先给出了命令模式的定义,通过吃饭的例子给出了使用命令模式实现遥控器设计思路,最后还提到了撤销命令和一个命令实现多个命令的做法。

    demo地址,欢迎star

    相关文章

      网友评论

        本文标题:9、HeadFirst--命令模式(Swift)

        本文链接:https://www.haomeiwen.com/subject/nhdnmqtx.html