设计模式之命令模式

x本文是对设计模式之禅一书中命令模式一章的总结与整理。

1. 定义

学习任何一个东西,先把概念搞清楚是第一要务。命令模式初看,其表达的意思类似于面向对象的概念,我说一件事,你完成它就可以了。书中给出的定义是:将一个请求封装成对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。好长的定义,反正我没看懂,不知道你是不是看懂了。其通用类图如下:

类图.png

2. 解析

某天你成为了公司的总经理、一把手,你手下有个叫波哥的部门经理,你对波哥说,让小五写一份提薪报告,我们来实现这个功能。

首先定义小五这个干活的:


public class Fifth {

    public void raiseSalary(){
        System.out.println("老板,我是小五,这个是我的提薪报告....");
    }
}

接下来,我们定义波哥这个角色,让他去通知小五干活:



public class Wavebrother {
    public void tellFifthDoSomething(){
        Fifth fifth=new Fifth();
        fifth.raiseSalary();
    }
}

再者你是经理得下命令啊:


public class Client {
    public static void main(String[] strings) {
        Wavebrother wavebrother = new Wavebrother();
        wavebrother.tellFifthDoSomething();
    }
}

结果如期而至,比较简单的一个程序,不上结果图了。我们再看这个程序有问题么?没问题,它完全能胜任我们的工作与目的,对吧?可是这里边难道就没有问题么,再仔细看看一下。。。

先让眼睛休息五分钟。。。。

总经理为什么要去指定某个部门的具体员工去干什么事呢?这个至少是不正规的一种管理方式,否则“直属上级"这个词还有什么意义呢?从另外一个角度说,假设你的公司有10000个人,难道你要通知波哥时,要告诉波哥这10000个人的姓名么?

有些人说,有道理。这样,我们再加一个命令类,专门用于接收命令,在这里指接收波哥的命令。

定义接口:


public interface ICommand {
    public void execute();
}

接下来定义具体的命令实现者,仔细想想,在这个具体的命令实现者中,我们需要把命令执行人给他,不能光下命令没人干活啊,是不?这个类叫提薪命令类,如下:


public class RaiseSalaryCommand implements ICommand {
    
    //具体的命令执行者---干活的人
    Fifth fifth =null;
    
    public RaiseSalaryCommand(){
        this.fifth=new Fifth();
    }
    
    //执行具体的命令
    @Override
    public void execute() {
        this.fifth.raiseSalary();
    }
}

这样,作为经理的你再下命令时就不用提干活的人是谁了:


public class Client {
    public static void main(String[] strings) {
        //定义调用者--需要通过他去执行你的命令
        Wavebrother wavebrother = new Wavebrother();
        //定义命令
        ICommand iCommand=new RaiseSalaryCommand();
        //执行命令
        iCommand.execute();
    }
}

执行结果如下:


结果.png

简单了吧,只需要找到你的下一级,给他下一道命令,就让他执行去吧,具体怎么执行那是他的事情了, 作为总经理的你无须去关心。是不是呢?

以上的方式中,作为经理的你是不需要知道具体的干活人员是谁的,可某些情况下我必须要知道干活的人是谁,比如某天你派小四签一个合同,但合同最终没成,那这种情况下你在下命令时就会用到这个人,此种情况就不再举例了。

3. 定论

根据以上的分析,这个类图是有问题的,因为经理你需要找一个调用者去调用一条命令的,修改后的类图如下:


类图2.png

其中这里的第一种对应你需要传递具体干活人(小四)的情况,第二种对应你直接下命令的情况的情况。
再看来书中的定义:将一个请求封装成对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。分析如下:请求就是指的我们这里的命令类,不同的请求应着不同的命令,把客户端参数化就是给命令传递干活的人(你可选择不传递),对请求排队就是在调用者中(这里指波哥)定义一个命令集合,再把所有命令收集到之后,统一依次执行,命令的撤销与恢复可以把它们理解成与提薪命令类相平行两个具体的命令类,一个是撤销,一个是恢复。

推荐阅读更多精彩内容