设计模式之模板方法模式实战解析

本文微信公众号「AndroidTraveler」首发。java

背景

最近在看《设计模式之禅》,为了可以更加深刻的理解设计模式,达到学以至用。
这边记录一下本身的一些感觉和见解,并结合具体代码实战来进行说明。算法

模板方法模式

但凡和设计模式挂上钩,咱们老是会以为「遥不可及」。
然而实际上,设计模式是基于大量实际代码的经验总结,它来自于实际的代码。
与其说「遥不可及」,其实它反而是比较「接地气」。
而模板方法模式相信你看完本篇文章以后,会发现,原来这就是模板方法模式,而后就去看你以前的代码了。设计模式

小例子初识模板方法模式

理解设计模式最好的方法就是经过项目开发中的实际场景来讲明。微信

你们作 Android 开发的时候写 Activity 应该都会看到下面相似代码吧?框架

private void getIntents() {
    // 从 Intent 获取传递过来的一些参数,设置到属性中
}

private void findViewById() {
    // 经过 findViewById 来初始化各个组件
}

private void setViews() {
    // 给组件设置监听或者初始状态
}

复制代码

假设我每一个界面都这样写,那么重复代码太多了,很不必。
虽然每一个方法具体的逻辑不同,可是都有这些操做。ide

这个时候咱们第一个想法就是继承,抽取出一个 BaseActivity。
而后将这些通用代码都放到了 BaseActivity 里面,子类再来覆写就能够了。spa

可是还有一个问题,那就是,我每次都须要写下面代码:设计

getIntents();
findViewById();
setViews();
复制代码

尤为是通用代码多的时候,有时候手误可能致使某些界面这三个方法调用顺序还不同。
那怎么办呢?咱们能够抽取出一个方法,这个方法表明了这三个方法统一的调用顺序,这样就不怕手误写错了。
而这个方法就是咱们的模板方法3d

public abstract class BaseActivity extends Activity {
    /** * 从 Intent 获取传递过来的一些参数,设置到属性中 */
    protected abstract void getIntents();

    /** * 经过 findViewById 来初始化各个组件 */
    protected abstract void findViewById();

    /** * 给组件设置监听或者初始状态 */
    protected abstract void setViews();

    /** * 模板方法 */
    final public void init() {
        getIntents();
        findViewById();
        setViews();
    }
}
复制代码

这样我后面的 Activity 均可以继承这个 BaseActivity,而后只须要调用 init 方法便可。
至于不一样的 Activity 的逻辑我再在三个方法里面各自实现便可。code

钩子方法

一听到这个词,是否是以为有点「高大上」,彷佛很 NB?
然而,听完我后面的讲述,你心里估计

咱们上面的方法里面,并非全部的 Activity 都有其余 Activity 传递数据过来的,所以 getIntents 这个方法不必定全部子类都要调用。
这个时候咱们能够提供一个钩子方法,改动部分代码以下:

/** * 模板方法 */
final public void init() {
    if (isGetIntents()) {
        getIntents();
    }
    findViewById();
    setViews();
}

/** * 钩子方法,是否须要设置数据,默认为 true * @return */
protected boolean isGetIntents() {
    return true;
}
复制代码

能够看到,经过钩子方法,当咱们默认须要获取数据时,什么都不用改动,若是咱们不须要获取数据,只须要覆写咱们的钩子方法 isGetIntents 并返回 false 便可。

好了,有了初步的印象以后,接下来就正式的加深了解吧。

定义

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
定义一个操做中的算法的框架,而将一些步骤延迟到子类中。使得子类能够不改变一个算法的结构便可重定义该算法的某些特定步骤。

简单的说就是父类定义了一个模板方法,在这个模板方法里面有一些特定的步骤。具体的步骤实现留给子类去处理。

父类的模板方法保持了各个子类的共性,模板方法里面的步骤使得每一个子类都有本身的个性。

通用代码实现

父类:

public abstract class AbstractPatternClass {
    /** * 基本方法,模板方法里面调用 */
    protected abstract void firstModule();
    /** * 基本方法,模板方法里面调用 */
    protected abstract void secondModule();

    /** * 模板方法,多个基本方法组合 */
    final public void templateMethod() {
        firstModule();
        secondModule();
    }
}
复制代码

具体子类:

public class ConcreteClass extends AbstractPatternClass {
    @Override
    protected void firstModule() {
        // TODO 子类实现本身的逻辑
    }

    @Override
    protected void secondModule() {
        // TODO 子类实现本身的逻辑
    }
}
复制代码

场景使用类:

public class PatternTest {
    public static void main(String[] args) {
        AbstractPatternClass abstractPatternClass = new ConcreteClass();
        // 调用模板方法
        abstractPatternClass.templateMethod();
    }
}
复制代码

钩子方法咱们上面已经说过了,相信聪明的你知道如何使用,这里就再也不赘述了。

注意点

父类中的基本方法尽可能设计为 protected 类型,符合迪米特法则。
父类中的模板方法通常设置为 final,不容许子类覆写。这样的目的一个是为了不子类恶意操做,一个是为了模板的共性。

应用

当你在写代码常常用到复制和粘贴快捷键时,你就要考虑是否是能够进行抽取。
当你修改一个地方的时候,发现其余地方也要连带修改,也须要考虑一下。
多个子类有公共方法,而且逻辑基本相同。
复杂的一些算法之类的,可让子类经过基本方法传递一些参数,核心逻辑放在模板方法里面。
重构项目的时候,也能够考虑一下把相同代码抽取到父类,经过钩子方法定制化模板。

结语

最后一点就是注意不要滥用设计模式,不要为了设计而设计

参考资料
设计模式之禅(第2版)

相关文章
相关标签/搜索