设计模式之模板方法模式(带思考)

源起

  不少设计模式比较简单,咱们在设计的时候或许都用过,只是不知道它们的名字而已。模板方法模式就是其中一种,相似的还有享元模式。或许听这名字你会以为不知所云,但等真正理解其原理的时候你确定会排着大腿说 :OMG,原来这就是模板方法模式。那读者不由要问了:为何这么多设计模式你不写,只写模板方法模式,是否是由于它简单呀?对呀,柿子要挑软的捏。开个玩笑,最主要缘由最近在工做中用到了这种设计模式,并且即便就这么简单的设计模式仍是几经周折才用上,仍是经验不够丰富。话很少说,放码过来。本文主要分为两个部分:java

  1. 模板方法模式相关内容的介绍。
  2. 扩展(一些碎言碎语,可跳过)。

什么是模板方法模式

脑筋急转弯

  小明将大象装进冰箱须要几个步骤,知道这个脑筋急转弯的人确定能够快速的答出有以下三个步骤:算法

  1. 小明把冰箱门打开
  2. 小明把大象放进去
  3. 小明把冰箱门关上

  用 java 实现以下设计模式

class XiaoMingStoreElephantToFridge {
    public void xiaoMingOpenFridgeDoor() {
        System.out.println("小明打开冰箱门");
    }
    
    public void xiaoMingPutIntoFridge() {
       System.out.println("小明将大象装进冰箱");
    }
    
    public void xiaoMingCloseFridgeDoor() {
       System.out.println("小明关上冰箱门");
    }
    
    //更多时候咱们用一个方法将以上步骤封装起来,在主方法中(client中)一次调用该方法便可。
    public void xiaoMingStoreElementToFridge(){
        xiaoMingOpenFridgeDoor();
        xiaoMingPutIntoFridge();
        xiaoMingCloseFridgeDoor();
    }
}
复制代码

  那小红把蚂蚁装进冰箱须要几个步骤呢,也是三步(不用把大象拿出来):post

  1. 小红把冰箱门打开
  2. 小红把蚂蚁放进去
  3. 小红把冰箱门关上

  用 java 实现以下学习

class XiaoHongStoreAntToFridge {
    public void xiaoHongOpenFridgeDoor() {
        System.out.println("小红打开冰箱门");
    }
    
    public void xiaoHongPutIntoFridge() {
       System.out.println("小红将蚂蚁装进冰箱");
    }
    
    public void xiaoHongCloseFridgeDoor() {
       System.out.println("小红关上冰箱门");
    }
    
    //更多时候咱们用一个方法将以上步骤封装起来,在主方法中(client中)一次调用该方法便可。
    public void xiaoHongStoreElementToFridge(){
        xiaoHongOpenFridgeDoor();
        xiaoHongPutInfoFridge();
        xiaoHongCloseFridgeDoor();
    }
}
复制代码

  那把 xxx 放入冰箱须要几步,这下你能够坚决果断的说出须要三步,Balabala。稍微有点设计思想的都知道咱们能够将小明/小红将大象/蚂蚁装进冰箱作进一步抽象,以下:网站

  1. 将冰箱门打开。
  2. 将xxx放进冰箱。
  3. 将冰箱门关闭。

  同理,java 类以下:this

class AbstractStoreSomethingToFridge {
    protected abstract void penFridgeDoor();
    
    protected abstract void putIntoFridge();
    
    protected abstract void closeFridgeDoor();
    }
    
    //更多时候咱们用一个方法将以上步骤封装起来,在主方法中(client中)一次调用该方法便可。
    public final void store() {
        penFridgeDoor();
        putIntoFridge();
        closeFridgeDoor();
    }
}
复制代码

  该抽象类只关心将物体放入冰箱的有哪些步骤,至因而谁/将什么放进冰箱该抽象类并不关心,这是子类该作的事。为了避免占用过多无用代码,子类这里再也不赘述。spa

模板方法模式的定义

  以上 AbstractStoreSomethingToFridge 就是一个模板类,其中 store() 方法就是模板方法。模板方法定义了一个方法的步骤,并容许子类为一个或者多个步骤提供实现。模板方法通常用 final 修饰,表示不容许在子类中重写该方法。若是模板方法能够被重写,那"模板"将毫无心义,或者说若是要重写模板方法那说明你要重定义一个模板类了。模板方法模式的 UML 图以下:设计

  其中:code

  1. AbstractClass 就是模板方法的抽象类,定义了一个模板方法,该方法规定了执行某一流程的规定步骤。
  2. PrimitiveOperation* 是子类必需要实现的步骤。

优缺点与应用场景

优势

1. 良好的封装性。把公有的不变的方法封装在父类,而子类负责实现具体逻辑。
2. 良好的扩展性:增长功能由子类实现基本方法扩展,符合单一职责原则和开闭原则。
3. 复用代码。
复制代码

缺点

1. 因为是经过继承实现代码复用来改变算法,灵活度会下降。
2. 子类的执行影响父类的结果,增长代码阅读难度。
复制代码

应用场景

1. 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;
2. 各子类中公共的行为应被提取出来并集中到一个公共父类中以免代码重复;
3. 控制子类的扩展。   
复制代码

总结与思考

模式总结

  模板方法模式是很是常见的模式,常见到咱们经常忽略它的存在。模板方法模式自己有许多种实现,好比在模板方法执行的先后加入钩子:preActionHook() 和 postActionHook() ,方便子类在模板方法执行以前或者执行后作一些动做。

  模板模式自己比较简单,难的是如何将要作的事情抽象出固定的、与业务无关的步骤。并非全部的业务都像将大象放入冰箱如此简单,若是稍微复杂一点的业务不少时候咱们很难跳出细节看到全貌。固然,学会抽象不是一朝一夕的事情,更多的是要学习、模仿和沉淀。

思考感悟

  看懂设计模式 UML 图不是目的,学会将设计模式应用于实际才是。我以为学习设计模式就像学习功夫,有两种境界:

  第一种是当咱们不知道任何设计模式的时候,咱们冥冥之中也会用。这时候咱们就须要学习、刻意的模仿,将本身知道/不知道的设计方法都往设计模式上套。这一种是记住设计模式。

  第二种是当咱们知道全部设计模式的时候,咱们再也不刻意的将设计方法套到各类设计模式上,也能写出高内聚低耦合的代码,作到手中无剑心中有剑。这一种是忘记设计模式。

  目前我还处在第一种境界中苦苦挣扎,但愿各位和我在学习设计模式乃至其余知识都能达到第二种境界。

扩展(可跳过)

实战经验

  最近在负责内部门户网站消息的推送系统,须要作各类推送:日推/周推/自定义推/事件推等,通过几回思考和重构将推送流程简化为如下几个步骤:

  1. 收集须要推送的内容,放入推送模型。
  2. 将推送模型与 velocity 模板集合。
  3. 获取接收推送的人信息。
  4. 调用底层通道发送消息推送(站内信/邮件/钉钉等)。

  这种场景下能够定义一个模板方法模型的抽象类:

class AbstractPush {
    protected void collectModelData();
    
    protected void generateViewContent();
    
    protected void collectReceivers();
    
    //无需子类实现
    private void sendByChannel() {
    }
    
    public final void push() {
        //通用固定模板代码
        collectModelData();
        generateViewContent();
        collectReceivers();
        sendByChannel();
    }
}
复制代码

  接着定义子类,并在在 client 中调用子类 push 方法便可。只要知道将推送过程简化为以上几个步骤,套用模板方法模式便水到渠成了。

有限状态机模式

  模板方法模式中的模板方法每每步骤是固定的且模板方法也是被 final 修饰的。结合有限状态机的定义,若是:

  1. 模板类中定义起始状态和结束状态,其余状态由子类来控制。
  2. 模板方法中的每一个步骤(除了开始和结束)的扭转由子类的状态来控制。
  3. 模板方法中的步骤数量在子类中能够控制。

  那模板方法模式就转变成了有限状态机模式,伪代码以下:

public class AbstractStateachine() {
    
    protected Integer state;
    protected void onStart();
    protected abstract void onAction();
    protected void onEnd();
    
    protected final onRun() {
        switch(state) {
            case START:
                onStart();
                this.state = ACTION;
                break;
            case END:
                onEnd();
                break;
            default:
                onAction();
                break;
        }
    }
}
复制代码

  这样只要在子类中定义更多的状态,同时重写 onAction() 方法便可,可能以下:

public class MyStateMachine() {
    //...其余省略
    
    protectd void onAction() {
        case CUSTOM_STATE_1:
            onCustomState1Action();
            this.state = CUSTOM_STATE_2;
            break;
        case CUSTOM_STATE_2:
            onCustomState1Action();
            this.state = END;
            break;
          default:
            this.state = END;
            break;
    }
    
    private onCustomState1Action() {
        //balabala1
    }
    
    private onCustomState2Action(){
        //balabala2
    }
}
复制代码

  固然,以上只是个人一个简略的想法,有错误的地方还请指正。后面还会对有限状态机模式作一个系统的学习和整理。

引用

  1. 个人Java设计模式-模板方法模式
  2. 模板方法模式(Template Method) - 最易懂的设计模式解析
相关文章
相关标签/搜索