关于命令模式的误区,你知道了吗

前言

最近项目开发需要用到命令模式,之前没学过,就过去研究了下。但是,理解后发现网上对于命令模式的讲解大多晦涩难懂,并且在选例时容易让人进入一个误区。

所以,干脆自己写篇命令模式的讲解,用于自我总结,当然,若能帮上各位同学,那就是赚了。

命令模式

首先我们需要清楚命令模式的使用场景,更多是用于存储行为操作。就像你想记录用户各种行为,将其写入日志中。但是,跟写日志又有点稍微不同的是,写日志是一次性行为,一旦写入文件中,就抛弃该行为了,而命令模式是把这些行为记录在内存中,方便后续对于这些行为进行管理。

例如在画图的时候,你画了一条直线,然后你觉得不够直,就将其取消了,但是后面想想,这直线感觉还行,又将其还原了。画直线是一种行为,而一旦你存储了这种行为,就很方便你进行撤销或者重制的功能了。

说了这么多,感觉可能还有点难理解,我们就以写字板作为一个🌰进行解读:

写个写字板的类:WordPad.kt ,里面含有绘画矩形和绘画圆形的功能:

class WordPad {
    fun drawRect(){
        println("绘画了一个矩形")
    }
    fun drawCircle(){
        println("绘画了一个圆形")
    }
}

这时,如果我们想绘画个矩形和圆形,该怎么做?

WordPad().drawRect() ? WordPad().drawCircle() ?

当然不能这样做,这样做的话,功能是可以实现,但是我们很难去存储这种行为,所以,我们需要定义一个接口,专门去执行某种行为,然后就可以通过 List<接口> 去存储这些行为:

interface ICommand {
    fun execute()
}

绘画矩形行为:

class RectCommand(val wordPad: WordPad) : ICommand {
    override fun execute() {
        wordPad.drawRect()
    }
}

绘画圆形行为:

class RectCommand(val wordPad: WordPad) : ICommand {
    override fun execute() {
        wordPad.drawRect()
    }
}

所以,想绘画矩形或者圆形该这样做:

        val wordPad = WordPad()
        RectCommand(wordPad).execute() // 绘画矩形
        CircleCommand(wordPad).execute() // 绘画圆形

日志输出:

I/System.out: 绘画了一个矩形
I/System.out: 绘画了一个圆形

好了,现在我们需要去存储行为了:

object Invoker {
    val commandList = mutableListOf<ICommand>()

    fun executeCommand(command: ICommand){
        command.execute()
        commandList.add(command)
    }
}

这样,后续的行为都由 Invoker 代替执行:

        val wordPad = WordPad()
        Invoker.executeCommand(RectCommand(wordPad)) // 绘画矩形
        Invoker.executeCommand(CircleCommand(wordPad)) // 绘画圆形

同时,由于 Invoker 已经存储了这些行为,我那么就可以对这些行为进行管控了,例如重复用户第一次行为,或者重复用户最后一次行为,或者重复用户所有行为:

object Invoker {
    val commandList = mutableListOf<ICommand>()

    fun executeCommand(command: ICommand) {
        command.execute()
        commandList.add(command)
    }

    // 重复所有行为
    fun repeat() = commandList.forEach {
        it.execute()
    }
    // 执行第一次
    fun doFirst() = commandList.first().execute()
    // 执行最后一次
    fun doLast() = commandList.last().execute()
}
        val wordPad = WordPad()
        Invoker.executeCommand(RectCommand(wordPad)) // 绘画矩形
        Invoker.executeCommand(CircleCommand(wordPad)) // 绘画圆形
        println("重复用户全部行为:")
        Invoker.repeat()
        println("重复用户第一次行为:")
        Invoker.doFirst()
        println("重复用户最后一次行为:")
        Invoker.doLast()
I/System.out: 绘画了一个矩形
I/System.out: 绘画了一个圆形
I/System.out: 重复用户全部行为:
I/System.out: 绘画了一个矩形
I/System.out: 绘画了一个圆形
I/System.out: 重复用户第一次行为:
I/System.out: 绘画了一个矩形
I/System.out: 重复用户最后一次行为:
I/System.out: 绘画了一个圆形

误区

初步看的话,会发现上面讲的命令模式是没什么问题的,确实也是可以这样实现的。但是,它却是有一个很容易让人理解错误的误区,那就是 WordPad。

因为对于命令模式来说,最核心的问题在于对命令的封装以及管控,所以,即使去掉了 WordPad 也不会有任何影响!

下面我们把 RectCommand 和 CircleCommand 也相应改下:

class RectCommand : ICommand {
    override fun execute() {
        println("绘画了一个矩形")
    }
}
class CircleCommand : ICommand {
    override fun execute() {
        println("绘画了一个圆形")
    }
}

执行代码也改下:

        Invoker.executeCommand(RectCommand()) // 绘画矩形
        Invoker.executeCommand(CircleCommand()) // 绘画圆形
        println("重复用户全部行为:")
        Invoker.repeat()
        println("重复用户第一次行为:")
        Invoker.doFirst()
        println("重复用户最后一次行为:")
        Invoker.doLast()

会发现跟输出结果并没有什么不同。因为对于 Invoker 而言,它无需知道每个命令的具体执行细节,它只负责调用和记录即可。但是,由于大部分的命令模式的讲解文都会从类似 WordPad 的类进行说起,就会让人感觉,好像命令模式是跟某个工具类进行绑定一样,还可能会感觉,该工具类只能存在一个。

思考与探索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值