命令模式,通常指的是一个对象向另一个对象发送信息指令的行为模型,比如父母命令孩子写作业、将军命令士兵进攻等。我们经过分析拆解方法会得到三个模块,首先得有命令发送方,接着是被传递的命令本身,最后就是命令的接收执行方了。那么,这样拆解到底有什么好处?让我们先来看一个最简单的例子,电灯泡。

既然是电灯那一定对应通电和断电的行为接口了,两个接口方法互斥,我们就叫它Switchable吧。

1 public interface Switchable {//电器接口
2    //通电
3    public void on();
4    //断电
5    public void off();
6
7 }

对于具体的灯泡实现类,必然是通电亮,断电灭。

 1 public class Bulb implements Switchable {23    @Override4    public void on(){5        System.out.println("通电,灯亮。");6    }   78    @Override9    public void off(){
10        System.out.println("断电,灯灭。");
11    }
12
13 }

同样地,我们再增加一个设备,如果是风扇的话则是通电转,断电停。

 1 public class Fan implements Switchable{23    @Override4    public void on() {5        System.out.println("通电,风扇转动。");6    }78    @Override9    public void off() {
10        System.out.println("断电,风扇停止。");
11    }
12
13 }

我们该如何操作呢?来吧,直接用电线接通电源。

 1 public class Client {23    public static void main(String[] args) {45        System.out.println("===客户端用【电线】直接操作灯泡===");6        Bulb bulb = new Bulb();7        bulb.on();8        bulb.off();9        /*打印输出:
10            ===客户端用【电线】直接操作灯泡===
11            通电,灯亮。
12            断电,灯灭。
13        */
14    }
15
16 }

也许用户是个糙人,直接用导线给通电了,简单粗暴,虽然没有错,但这看上去与设计模式没有任何瓜葛。为了体现出模式的优越性,我们需要让系统进化得更高级一些,于是我们决定加入另一个模块,开关控制。

 1 public class Switcher {23    // 此开关与灯耦合,无法替换为其他电器。4    // private Bulb bulb = new Bulb();56    // 此开关与电器接口耦合,可任意替换电器。7    private Switchable switchable;89    // 替换电器方法
10    public void setSwitchable(Switchable switchable) {
11        this.switchable = switchable;
12    }
13
14    // 按键事件绑定
15
16    // 按钮“开”按下
17    public void buttonOnClick() {
18        System.out.println("按下开……");
19        switchable.on();
20    }
21
22    // 按钮“关”按下
23    public void buttonOffClick() {
24        System.out.println("按下关……");
25        switchable.off();
26    }
27
28 }

这里的开关就类似一个控制器了,有“开”和“关”两个按键分别绑定了设备的“通电”与“断电”行为方法。需要特别注意的是,如果在第4行我们声明地是灯泡,那么无疑这个开关与灯泡就绑定死了,也就是强耦合了,所以第7行我们声明的是Switchable接口引用,并提供第10行的替换电器方法给外界注入任何的设备。好了,我们换个方式运行程序。

 1 public class Client {23    public static void main(String[] args) {45        System.out.println("===客户端用【开关】操作电器===");6        Switcher switcher = new Switcher();78        switcher.setSwitchable(new Bulb());//灯泡接入开关。9        switcher.buttonOnClick();
10        switcher.buttonOffClick();
11        switcher.setSwitchable(new Fan());//风扇接入开关。
12        switcher.buttonOnClick();
13        switcher.buttonOffClick();
14
15        /*打印输出:
16            ===客户端用【开关】操作电器===
17            按下开……
18            通电,灯亮。
19            按下关……
20            断电,灯灭。
21            按下开……
22            通电,风扇转动。
23            按下关……
24            断电,风扇停止。
25         */
26    }
27
28 }

这次看上去功能强大多了,开关可以随意地接入灯泡或者风扇,注入的是谁那么开关按钮直接就作用于谁,对于设备我们还可以继续扩展,设计模式开始体现优势了。等等,这个模式好像似曾相识的感觉,没错,这正是之前讲过的策略模式,可这跟命令模式有什么关系?不要着急,我们先看下这个开关策略模式是否满足了我们的需求。

假设我们的设备不断扩展,比如有了电视机,收音机等等设备,它们不止是开关通电这种简单行为模式了,还可以有转换频道、变音量等等更多的行为。

那么我们的简单开关还能满足对接电视机的琳琅满目的功能吗?注意之前我们的开关Switcher类中第7行代码:private Switchable switchable; 虽然可以替换设备,但只能是Switchable设备对象,这就与”可开关设备接口“强耦合了,也就是说它只能控制“灯泡或风扇”,并不能控制”电视或收音机”。

同时另一端我们的开关控制器也在不断进化,发展出了更多功能控制器、无线遥控器、甚至是手机App控制。

所以,如何把控制器与设备完全给拆解开势在必行,此时命令模式粉墨登场。现在我们得新定义出一组”命令“模块把控制器(发令者)与设备(执行者)彻底解耦,就以电视机和遥控器举例说明吧。

 1 public interface Device extends Switchable{23    // 频道+4    public void channelUp();5    // 频道-6    public void channelDown();7    // 音量+8    public void volumeUp();9    // 音量-
10    public void volumeDown();
11
12 }

注意代码第1行的接口继承,我们的高级设备接口则遗传了之前的简单通断电接口,并新增了调节频道和音量4个功能。接下来是电视机与收音机实现类。

 1 public class TV implements Device {23    @Override4    public void on(){5        System.out.println("电视机启动");6    }   78    @Override9    public void off(){
10        System.out.println("电视机关闭");
11    }
12
13    @Override
14    public void channelUp() {
15        System.out.println("电视机频道+");
16    }
17
18    @Override
19    public void channelDown() {
20        System.out.println("电视机频道-");
21    }
22
23    @Override
24    public void volumeUp() {
25        System.out.println("电视机音量+");
26    }
27
28    @Override
29    public void volumeDown() {
30        System.out.println("电视机音量-");
31    }
32 }
 1 public class Radio implements Device {23    @Override4    public void on(){5        System.out.println("收音机启动");6    }   78    @Override9    public void off(){
10        System.out.println("收音机关闭");
11    }
12
13    @Override
14    public void channelUp() {
15        System.out.println("收音机调频+");
16    }
17
18    @Override
19    public void channelDown() {
20        System.out.println("收音机调频-");
21    }
22
23    @Override
24    public void volumeUp() {
25        System.out.println("收音机音量+");
26    }
27
28    @Override
29    public void volumeDown() {
30        System.out.println("收音机音量-");
31    }
32 }

没什么好说的,下来是解耦的重点了,我们在策略模式的基础上又增加一层中间模块,开始编写命令模块代码,首先是命令接口。

1 public interface Command {
2
3    //执行命令操作
4    public void exe();
5
6    //反执行命令操作
7    public void unexe();
8
9 }

命令接口有执行操作与反执行操作两个标准功能,然后定义其命令实现类,开关机命令、频道转换命令、以及音量调节命令。

 1 public class SwitchCommand implements Command {23    private Device device;// 此处持有高级设备接口。45    public SwitchCommand(Device device) {6        this.device = device;7    }89    @Override
10    public void exe() {
11        device.on();// 执行命令调用开机操作
12    }
13
14    @Override
15    public void unexe() {
16        device.off();// 反执行命令调用关机操作
17    }
18
19 }
 1 public class ChannelCommand implements Command{23    private Device device;45    public ChannelCommand(Device device) {6        this.device = device;7    }89    @Override
10    public void exe() {
11        device.channelUp();
12    }
13
14    @Override
15    public void unexe() {
16        device.channelDown();
17    }
18
19 }
 1 public class VolumeCommand implements Command{23    private Device device;45    public VolumeCommand(Device device) {6        this.device = device;7    }89    @Override
10    public void exe() {
11        device.volumeUp();
12    }
13
14    @Override
15    public void unexe() {
16        device.volumeDown();
17    }
18
19 }

代码很简单,但是系统模组相对复杂,所以一定要搞清楚各模块间关系再继续。最后一个模块是遥控器类,也就是命令发送方了。我们保持简单,遥控器集成了OK按键以及上下左右方向键。

 1 public class Controller {2    private Command okCommand;3    private Command verticalCommand;4    private Command horizontalCommand;56    // 绑定OK键命令7    public void bindOKCommand(Command okCommand) {8        this.okCommand = okCommand;9    }
10
11    // 绑定上下方向键命令
12    public void bindVerticalCommand(Command verticalCommand) {
13        this.verticalCommand = verticalCommand;
14    }
15
16    // 绑定左右方向键命令
17    public void bindHorizontalCommand(Command horizontalCommand) {
18        this.horizontalCommand = horizontalCommand;
19    }
20
21    // 开始按键映射命令
22    public void buttonOKHold() {
23        System.out.print("长按OK按键……");
24        okCommand.exe();
25    }
26
27    public void buttonOKClick() {
28        System.out.print("单击OK按键……");
29        okCommand.unexe();
30    }
31
32    public void buttonUpClick() {
33        System.out.print("单击↑按键……");
34        verticalCommand.exe();
35    }
36
37    public void buttonDownClick() {
38        System.out.print("单击↓按键……");
39        verticalCommand.unexe();
40    }
41
42    public void buttonLeftClick() {
43        System.out.print("单击←按键……");
44        horizontalCommand.unexe();
45    }
46
47    public void buttonRightClick() {
48        System.out.print("单击→按键……");
49        horizontalCommand.exe();
50    }
51 }

这个遥控器持有三个命令组件,并且于第7行开始定义命令绑定方法,最后从第22行开始定义各按键触发方法并映射到相应的命令操作上。可以看到,控制器对设备一无所知,也就是它上面不再绑定有任何设备了,而是只绑定命令。最后,客户端又换了一种方式运行程序。

 1 public class Client {23    public static void main(String[] args) {4        System.out.println("===客户端用【可编程式遥控器】操作电器===");5        Device tv = new TV();6        Device radio = new Radio();7        Controller controller = new Controller();89        //绑定【电视机】的【命令】到【控制器按键】
10        controller.bindOKCommand(new SwitchCommand(tv));
11        controller.bindVerticalCommand(new ChannelCommand(tv));//上下调台
12        controller.bindHorizontalCommand(new VolumeCommand(tv));//左右调音
13
14        controller.buttonOKHold();
15        controller.buttonUpClick();
16        controller.buttonUpClick();
17        controller.buttonDownClick();
18        controller.buttonRightClick();
19
20        /*打印输出:
21            ===客户端用【可编程式遥控器】操作电器===
22            长按OK按键……电视机启动
23            单击↑按键……电视机频道+
24            单击↑按键……电视机频道+
25            单击↓按键……电视机频道-
26            单击→按键……电视机音量+
27        */
28
29        //绑定【收音机】的【命令】到【控制器按键】
30        controller.bindOKCommand(new SwitchCommand(radio));
31        controller.bindVerticalCommand(new VolumeCommand(radio));//上下调音
32        controller.bindHorizontalCommand(new ChannelCommand(radio));//左右调台
33
34        controller.buttonOKHold();
35        controller.buttonUpClick();
36        controller.buttonUpClick();
37        controller.buttonRightClick();
38        controller.buttonDownClick();
39
40        /*打印输出:
41            长按OK按键……收音机启动
42            单击↑按键……收音机音量+
43            单击↑按键……收音机音量+
44            单击→按键……收音机调频+
45            单击↓按键……收音机音量-
46        */
47
48    }
49
50 }

很显然,客户端可以肆意妄为地组装各个模块了,也就是说可以遥控电视,也可以遥控收音机,或许绑定上下键调音量,或许是换成左右键调音量,甚至可以定义一个宏命令去控制灯泡的切换开关实现一种霓虹灯闪烁的效果(读者可以思考怎样实现),而对于控制器端本身,同样可以继续扩展,或许干脆替换个游戏手柄或者键盘,一样可以发号施令。

至此,发令控制方与接受执行方完全被拆解开,这让我们实现了对各模块的自由扩展,对指令映射、设备绑定的灵活操控,松散的系统得以成就繁多模块解耦的最终目的。

JAVA设计模式什么鬼(命令模式)——作者:凸凹里歐相关推荐

  1. Java 设计模式之命令模式

    一.了解命令模式 1.1 什么是命令模式 命令模式将"请求"封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作.这种说法比较难以理解,换种说法 ...

  2. JAVA设计模式之命令模式

    将请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化:对起那个请求进行排队或记录请求日志. 命令模式告诉我们可以为一个操作生成一个对象并给出它的一个execute(执行)方法. Comman ...

  3. Java设计模式:命令模式

    一.命令模式(Command)的定义 将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化:对请求排队或记录日志,以及支持可撤销的操作,将"发出请求的对象"和"接 ...

  4. 玉帝传美猴王上天,大闹天宫之Java设计模式:命令模式

    命令模式 示例 改进代码 命令模式 定义 意图 主要解决问题 何时使用 优缺点 玉帝传美猴王上天 命令模式和策略模式的区别 示例 系统需要设计一个命令行界面,用户可输入命令来执行某项功能,系统的功能会 ...

  5. 【java设计模式】命令模式——电视机是请求的接收者,遥控器是请求的发送者,遥控器上有一些按钮,不同的按钮对应电视机的不同操作。

    命令模式(Command Pattern):将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化:对请求排队或者记录请求日志,以及支持可撤销的操作. 命令模式包含如下角色: Comman ...

  6. JAVA设计模式什么鬼(单例)——作者:凸凹里歐

    之前我们讲过面向对象以及封装.继承.多态三大特性,底子打好了那我们就把设计模式一个个拆开来看看到底都是神些什么鬼,我们先从简单的单例说起吧.单例,顾名思义,整个系统其实就只有一个实例存在,不能再多,否 ...

  7. JAVA设计模式什么鬼(享元)——作者:凸凹里歐

    元,始也,本初,根源之意,计算机中的二进制"元"其实就1和0,这两个东西组合起来有无穷无尽的可能,这便形成了计算机中的大千世界,正如"阴"和"阳&qu ...

  8. JAVA设计模式什么鬼(终章)——作者:凸凹里歐

    设计模式已经全部讲完,习得23种招式后我们也该归纳总结一下了,实践需与理论相结合才能更好地举一反三,灵活运用.设计模式到底是什么?它是对整个软件系统的拆分,组装,并决定模块间关系以及如何互动.通信的某 ...

  9. JAVA设计模式什么鬼(责任链)——作者:凸凹里歐

    曾经有这么一些零散的功能节点,他们各自承担各自的义务,分工明确,各司其职.为了更高效,更完整地解决客户的问题,他们发扬团队精神,互相串联起来形成一个有序的责任传递链表,于是责任链模式诞生了.当然,它的 ...

最新文章

  1. laravel auth(api)-attempt 返回false_3分钟短文:Laravel命令行参数和可选项分不清?怎么可能...
  2. 微软Hololens设备 浅分析
  3. JavaScript中的剪贴板(clipboardData)
  4. boost::range_reverse_result_iterator相关的测试程序
  5. 【Nutch2.2.1源代码分析之4】Nutch加载配置文件的方法
  6. codeforces1471 D. Strange Definition
  7. spring 注释_Spring @Value注释
  8. mac java 版本_Mac 下 Java 多版本切换
  9. 我是如何在 10 分钟内搞砸 IT 面试的
  10. android 图片查看源码,Android 简单的图片查看器源码
  11. 2018.5.15Html标签初学
  12. 互联网对实体经济的三轮冲击
  13. 批量ping及telnet工具
  14. FLUENT 汽车流场仿真分析-结构网格划分视频教程
  15. python交互式培训网站对比(风变编程、指尖编程、扇贝编程)
  16. 【编译原理】 CS143 斯坦福大学公开课 专栏总揽
  17. JavaServer Faces 2.0 can not be installed解决方案
  18. 曾国藩【挺经】全文 不错的文章,值得学习!
  19. 自动关闭当前的Fragment返回上一个Fragment该如何实现
  20. 200左右哪款蓝牙耳机值得入手?双11小白新手避雷高性能蓝牙耳机

热门文章

  1. C++ 实现俄罗斯方块!!!
  2. 深度玄学-实战开发步骤
  3. 一次网络请求是如何实现的
  4. pdc 半圆_PDC。 我已经准备好进行革命了。 打动我。 把我吹走
  5. 2019年3月计算机二级考试题库百度云,2019年3月计算机等级考试题库-二级MS Office试题...
  6. Docker 从入门到实践系列三 - Docker 常用命令,java高级开发工程师面试问题
  7. PMSM弱磁控制的电压、电流极限圆幅值的含义
  8. js节点操作自定义属性
  9. Python开发GUI实战:图片转换素描画工具!
  10. vue项目利用cesium框架加载倾斜摄影OSGB三维数据(详细)