命令模式

一、概述

一般问题:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

核心方案:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

设计意图:一个对象调用另一个对象方法,或者换一种说法,一个对象向另一个对象发起一个请求,这在程序中是再正常不过的现象。然而设计模式的脾气向来都是“一言不合就拆,拆,拆”,这次拆不是因为请求内容多变,而是其执行时机多变。换句话说,我知道我要发起一个请求,但是什么时候执行、由谁来执行、是否会反悔都是不确定的。所以,干脆把请求对象化,想仍给谁就仍给谁,想什么时候扔就什么时候扔,不想要了就丢掉。这就是命令模式的初衷:把行为请求者与行为执行者解耦。

命令模式类图如下:

 

  • Invoker是请求者,负责调用命令对象发起执行
  • Receiver是接收者,负责请求具体内容的执行

我们以生活中点菜为例,说明命令模式的实际运用:

看上图,如果不使用命令模式,而是客户直接到厨房要求厨师做自己喜欢的宫保鸡丁,可以吗?当然可以,而且最有效率。但同时也损失了可能的服务升级,比如:

  1. 客户不能预约下单
  2. 客户必须知道哪个师傅会做宫保鸡丁
  3. 客户下单后无法撤回

如果采用命令模式,以上问题可以轻松解决。客户和厨师解耦了,而且可以对菜单有更灵活的控制。


 

二、应用实战

在Android中我们几乎每天都在使用命令模式,典型的就是Runnable:一个Runnable就是一个Command;Thread扮演Invoder的角色,负责发起执行Runnable;Receiver可以是系统中的任何类,通常也可以作为Runnable的成员变量或者以参数形式传入Runnable。Runnable类图如下:

 

以锁屏图案解锁中Runnable的用法为例:输入错误图案后,我们希望延迟几秒后清空错误图案,以便用户可以重新输入。

定义具体Runnable :

    /**
     * Useful for clearing out the wrong pattern after a delay
     */
    private Runnable mCancelPatternRunnable = new Runnable() {
        @Override
        public void run() {
            mLockPatternView.clearPattern();
        }
    };

匹配错误后:

        private void onPatternChecked(int userId, boolean matched, int timeoutMs,
                boolean isValidPattern) {
                    ...
                    //图案匹配失败
                    mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern);
                    //延迟执行Runnable
                    mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
                }
            }
        }

如果延迟时间未到,用户再次输入,则立刻清空错误图案,并且撤回已在延时的Runnable:

        @Override
        public void onPatternStart() {
            //撤回已经在延时的Runnable
            mLockPatternView.removeCallbacks(mCancelPatternRunnable);
            mSecurityMessageDisplay.setMessage("");
        }

三、总结

总结:命令模式是一种行为型设计模式,它针对程序中最常见的请求与执行,将请求的发起者与执行者解耦,同时给予命令更多的可控性。

优点:

  • 降低系统耦合
  • 新命令易于扩展

缺点:可能造成过多命令类

 

posted @ 2019-06-26 10:35  西贝雪  阅读(761)  评论(0编辑  收藏  举报