• 命令模式


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

    看下执行结果。

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

  • 相关阅读:
    Delphi中TFlowPanel实现滚动条效果
    Delphi组件开发-在窗体标题栏添加按钮(使用MakeObjectInstance(NewWndProc),并处理好多消息)
    减小Delphi的Exe文件大小(11种方法)
    画出太极图
    类似地图的以鼠标为中心缩放图像
    andoid x项目的优化 1
    ICON图标文件解析
    所有语言的Awesome(2)
    获取EIP(汇编语言直接给Delphi变量赋值)
    Qt Windows下链接子系统与入口函数(终结版)(可同时存在main和WinMain函数)
  • 原文地址:https://www.cnblogs.com/yulinfeng/p/5940704.html
Copyright © 2020-2023  润新知