命令模式

当学了这个命令模式后,又一次体会到编程的艺术,明明一个看似很简单的事,却要用“复杂”的方法来实现,就像在之前我多次说到的,其实并不是“复杂”,并不是“难”,而是自己基本功太弱,这些看似“复杂”的设计并不是故弄玄机卖弄,仔细学习过后才发现之精妙。比如今天要说的命令模式,书中举的例子非常典型。去路边摊买烧烤,我是客户,老板是烤烧烤的,这个可以简单画一下UML类结构图。

这是我们一贯的思维,烤羊肉、烤鸡翅作为烤烧烤者的两个类,我作为买烧烤者(客户端)直接通过烤烧烤者类调用“烤羊肉”、“烤鸡翅”方法,这实际上是非常紧的耦合,客户多了,烧烤摊就会乱。所以我们要想如何来将代码解耦呢?同样,还是这个例子,有路边摊,但是也有烧烤店,在烧烤店里服务员给我们一个菜单,服务员将我们点好的菜单再转交给烤串师傅,在这期间我们不必关心是谁烤的,我们也不会一直盯着他烤串,我们吃多吃少吃咸吃淡这一切在菜单里都有记录,我们坐等上烤串就可以了。通过烧烤店我们就可以引申出一个设计模式——命令模式。

我们思考,“烧烤店模式”在中间多了一个服务员,通过服务员发送“命令”给师傅来给我们烤串,具体的烤串过程,我不关心服务员也不关心。

一个基本的雏形好像就出来了,我们再继续,我们把烤羊肉、烤鸡翅当做一个一个命令,就可以将命令抽象出来了。

这好像差不多了,不过我们的烤串师傅呢?我们说了烤串师傅才是具体负责烤羊肉、烤鸡翅的,所以就是烤羊肉、烤鸡翅依赖于烤串师傅。

通过上面的UML类结构图,我们用代码来实现烧烤店的烤串场景。

首先我们把命令接口改为抽象类,因为我们要传递一个具体的烤串者进去。

 1 package day_8_command;
 2 
 3 /**
 4  * 命令抽象类,通过构造函数可提供具体的烤串师傅
 5  * @author 余林丰
 6  *
 7  * 2016年10月8日
 8  */
 9 public abstract class AbstractCommand {
10     protected Barbecurer barbecurer;
11     
12     public AbstractCommand(Barbecurer barbecurer){
13         this.barbecurer = barbecurer;
14     }
15     
16     public abstract void excuteCommand();
17 }

实现一个具体的命令类即可。

 1 package day_8_command;
 2 
 3 /**
 4  * 具体命令
 5  * @author 余林丰
 6  *
 7  * 2016年10月9日
 8  */
 9 public class Command1 extends AbstractCommand {
10 
11     /**
12      * @param barbecurer
13      */
14     public Command1(Barbecurer barbecurer) {
15         super(barbecurer);
16     }
17 
18     /* (non-Javadoc)
19      * @see day_8_command.AbstractCommand#excuteCommand()
20      */
21     @Override
22     public void excuteCommand() {
23         System.out.println("开始烤羊肉串");
24     }
25 
26     @Override
27     public String toString() {
28         return "烤羊肉串";
29     }
30 
31 }

接着是服务员负责从客户这里取回菜单向师傅喊命令烤串。

 1 package day_8_command;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * 服务员
 8  * @author 余林丰
 9  *
10  * 2016年10月8日
11  */
12 public class Waiter {
13     private List<AbstractCommand> order = new ArrayList<AbstractCommand>();
14     
15     /**
16      * 设置订单
17      * @param command 具体命令,也就是具体烤什么
18      */
19     public void setOrder(AbstractCommand command){
20         order.add(command);
21         System.out.println("增加订单:" + command.toString());
22     }
23     
24     /**
25      * 取消订单
26      * @param command
27      */
28     public void cancelOrder(AbstractCommand command){
29         order.add(command);
30         System.out.println("取消订单:" + command.toString());
31     }
32     
33     /**
34      * 通知全部执行
35      */
36     public void notifyX(){
37         for (AbstractCommand cmd : order){
38             cmd.excuteCommand();
39         }
40     }
41 }

具体的烤串师傅,在这里实际并没有用到它多少。

 1 package day_8_command;
 2 
 3 /**
 4  * 烤串师傅
 5  * @author 余林丰
 6  *
 7  * 2016年10月8日
 8  */
 9 public class Barbecurer {
10     public void action(){
11         System.out.println("开始执行");
12     }
13 }
 1 package day_8_command;
 2 
 3 /**
 4  * 客户端测试类
 5  * @author 余林丰
 6  *
 7  * 2016年10月9日
 8  */
 9 public class Main {
10 
11     /**
12      * @param args
13      */
14     public static void main(String[] args) {
15         Barbecurer boy = new Barbecurer();
16         Command1 command1 = new Command1(boy);
17         Waiter waiter = new Waiter();
18         waiter.setOrder(command1);
19         waiter.notifyX();
20     }
21 
22 }

看下执行结果。

image.png

一定要从UML类结构图开始到编码自己手动过一遍,这仅仅是学习,离实际应用还有很长的理解时间,就比如说在这里我们并没有利用多少“烤串者”这个类,但这不代表在命令模式下就不需要它。最后来总结下命令模式的优点:它能把请求一个操作的对象与知道怎么执行一个操作的对象分隔开。最后说个题外话,对于敏捷开发原则,当我们并不清楚一个系统是否需要命令模式时,我们不用绞尽脑汁去实现,在以后的系统版本中如果需要用到命令模式,再通过代码重构为命令模式。这就涉及敏捷开发和重构了,再将设计模式学习完过后接着就是聊聊重构。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 中介者模式

    学了这么多的设计模式,离真正的实际运用还有很长的路要走,理论是理论,理论结合实践才有用。今天继续吧,继续一个学习一个新的设计模式——中介者模式。 中介在现实生活...

    用户1148394
  • 似懂非懂的Comparable与Comparator

      Comparable与Comparator都是用于集合的排序,对于大多数人来说Comparator可能略微比Comparable要熟悉一点,类似下面这几句代...

    用户1148394
  • MyBatis源码解读(4)——SqlSession(上)

      在上一篇博客中提到MyBatis是如何实现代理类MapperProxy,并抛出了一个问题——是怎么执行一个具体的sql语句的,在文末中提到了MapperMe...

    用户1148394
  • 数据解读近20年青少年弑父弑母悲剧:关爱是我们共同的责任

    导读:4月21日,涉嫌弑母的北大学子吴谢宇被抓获,这距离他2015年7月11日残忍杀害自己亲生母亲谢天琴的日子已有1380天,而他行凶时还是一个未满21岁的青少...

    华章科技
  • 谈谈IE针对Ajax请求结果的缓存

    在默认情况下,IE会针对请求地址缓存Ajax请求的结果。换句话说,在缓存过期之前,针对相同地址发起的多个Ajax请求,只有第一次会真正发送到服务端。在某些情况下...

    蒋金楠
  • 总结----文本文件的编码格式

    若在2.x中使用中文,需要在python文件的首行加上如下格式,则python解释器便会以utf-8来处理此python文件,

    py3study
  • 译文 | 量化投资教程:投资组合优化与R实践

    本文由CDA作者库成员HarryZhu翻译,并授权发布。 CDA作者库凝聚原创力量,只做更有价值的分享。 ? Harry Zhu,擅长用Python和R进行数据...

    CDA数据分析师
  • Java漫谈6

    今天这篇想聊聊数组。 在聊数组之前先聊个别的,如果想在Java中实现一个 数字-月份 转换,那我该怎么做呢?就比如数字1代表了一月份,数字2代表了二月份…数字...

    用户1335799
  • 【每周一坑】3道练习题

    如题图所示,今天把论坛(crossin.me)的服务器迁移到一个很萌的云服务上,速度还可以。欢迎大家常来。 这里再次感谢 aresli 同学提供的服务器,让论坛...

    Crossin先生
  • VIM编辑命令的技巧

     1 简单替换表达式 :%s/four/4/g “%” 范围前缀表示在所有行中执行替换。

    阳光岛主

扫码关注云+社区

领取腾讯云代金券