模板方法设计模式

来源:互联网 发布:java购物商城源码 编辑:程序博客网 时间:2024/06/06 01:23

概览

概述

准备一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总成一个不可改变的模板方法。

如何实现

第一步

  1. 提供抽象基类;
  2. 在基类中提供一个final(final是为了防止算法逻辑被重写修改)的模板算法框架,这个算法框架所有子类必须共同遵循;
  3. 基本方法在基类中实现,使用private权限修饰,向下层隐藏细节;
  4. 提供抽象方法,延续到子类中实现;
  5. 可选的钩子方法,具体的子类决定是否挂钩以及如何挂钩。

第二步

  1. 实现基类中的抽象方法,完成个性化实现;
  2. 覆盖基类中的钩子方法(可选)。

特点

  1. 优点

    1. 封装性好;
    2. 复用性好;
    3. 屏蔽细节;
    4. 便于维护。
  2. 缺点

    1. Java中的单继承问题

应用场景

  1. 算法或者操作遵循相同罗逻辑;
  2. 重构时(相同代码抽取到一个基类中);
  3. 重要,复杂的算法以及核心算法的设计。

分析一个坐火车的情形

  1. 买票
  2. 去火车站
  3. 上车
  4. 目的地下车

如果要坐火车,这四步就是基本的流程,但是买票我可以有多种选择,可以去窗口买,可以在12306客户端买,这个细节根据每个人的习惯而不一样,所以基类不帮你实现,要定义一个抽象方法延续到子类中去实现,还有下车的目的地也不尽相同。但是其它两步都是确定的,这就可以在基类中实现。

代码实现

上基类的代码及注释:

/*** 乘坐火车的模板方法设计模式的基类*/public abstract class ByTrainTemplate {public final void byTrainTemplateMethod() {    buyTicket();    goToRailWayStation();    getOnTrain();    detrain();}/** * 买票的方式不确定,延续到子类中去实现 */protected abstract void buyTicket();/** * 都要去火车站乘车的,所以在基类中就可以实现 * 用private屏蔽细节 */private void goToRailWayStation() {    Log.e("template","去火车站");}protected void getOnTrain(){    Log.e("template","上车");}/** * 每个人目的地不同,所以基类不实现,交由子类去实现 */protected abstract void detrain();}

乘客A的坐车过程

/*** PassengersA的乘车方法*/public class PassengersA extends ByTrainTemplate {@Overrideprotected void buyTicket() {    Log.e("template","通过12306买的票");}@Overrideprotected void detrain() {    Log.e("template","去兰州旅游,在兰州下车");}}

乘客B的乘车过程

/*** PassengersB的乘车旅程*/public class PassengersB extends ByTrainTemplate {@Overrideprotected void buyTicket() {    Log.e("template","我在窗口买的票");}@Overrideprotected void detrain() {    Log.e("template","我要去青海看青海湖,我在青海下车");}}

调用一下试试看

    ByTrainTemplate passengersA = new PassengersA();    passengersA.byTrainTemplateMethod();    Log.e("template","-------------------------------------");    ByTrainTemplate passengersB = new PassengersB();    passengersB.byTrainTemplateMethod();

执行结果

 E/template: 通过12306买的票 E/template: 去火车站 E/template: 上车 E/template: 去兰州旅游,在兰州下车 E/template: ------------------------------------- E/template: 我在窗口买的票 E/template: 去火车站 E/template: 上车 E/template: 我要去青海看青海湖,我在青海下车

是不是实现了结果,又很完美的避免了重复代码。这个时候铁路家属不乐意了,根据政策自己是可以免费乘车的,根本不需要买票好不好,那怎么办?这个时候就用到hook也就是所谓的钩子方法了。

改造基类,增加了一个钩子:

/** * 乘坐火车的模板方法设计模式的基类 */public abstract class ByTrainTemplate {    public final void byTrainTemplateMethod() {        if (isNeedTicket()) {            buyTicket();        }        goToRailWayStation();        getOnTrain();        detrain();    }    /**     * 是否需要车票才能乘车     * 大部分是需要车票的,所以默认就给个true好了     * @return     */    protected boolean isNeedTicket() {        return true;    }    /**     * 买票的方式不确定,延续到子类中去实现     */    protected abstract void buyTicket();    /**     * 都要去火车站乘车的,所以在基类中就可以实现     * 用private屏蔽细节     */    private void goToRailWayStation() {        Log.e("template", "去火车站");    }    protected void getOnTrain() {        Log.e("template", "上车");    }    /**     * 每个人目的地不同,所以基类不实现,交由子类去实现     */    protected abstract void detrain();}

乘客C的乘车过程

/** * PassengersC的乘车过程 */public class PassengersC extends ByTrainTemplate {    @Override    protected void buyTicket() {        Log.e("template","我是铁路职工家属,不要票");    }    @Override    /**     * 覆盖钩子方法     */    protected boolean isNeedTicket() {        return false;    }    @Override    protected void detrain() {        Log.e("template","我在西安下车");    }}

调用乘客C的乘车过程方法

ByTrainTemplate passengersC = new PassengersC();passengersC.byTrainTemplateMethod();

看结果是不是不需要买票了

E/template: 通过12306买的票E/template: 去火车站E/template: 上车E/template: 去兰州旅游,在兰州下车E/template: -------------------------------------E/template: 我在窗口买的票E/template: 去火车站E/template: 上车E/template: 我要去青海看青海湖,我在青海下车E/template: -------------------------------------E/template: 去火车站E/template: 上车E/template: 我在西安下车

其实这种方法在Android开发中我们经常用到,比如抽取出一个BaseActivity和BaseFragment等等。