23种设计模式12---模板方法模式

模板方法模式

业务需求:汽车厂造悍马

假设我们是一个汽车公司,现在有客户来了,要求我们造悍马! 既然上级下来命令那就造呗,但是造悍马你得告诉我们汽车有什么功能啊,客户说了:“能启动车,能停止车,能响,能跑。”好,功能出来了,开始造汽车了。类图如下:
这里写图片描述

一、抽象模板模型

在抽象类中,我们定义了悍马模型都必须具有的特质:能够发动、停止,喇叭会响,引擎可以轰鸣,而且还可以停止

package com.chb.l.TemplateDesignPattern;
/**
一辆车的功能
    start
    engineboom
    run
    alarm
    stop
 */
public abstract class HummerModel {
    /**
     * 启动
     */
    public abstract void start();
    /**
     * 引擎轰鸣
     */
    public abstract void engineBoom();
    /**
     * 行驶
     */
    public abstract void run();
    /**
     * 遇到情况鸣笛
     */
    public abstract void alarm();
    /**
     * 停车
     */
    public abstract void stop();
}

二、高级定制嘛,每种型号的肯定有所不同

2.1、H320悍马

package com.chb.l.TemplateDesignPattern;
/**
 230系列车型
 */
public class Hummer320 extends HummerModel{

    @Override
    public void start() {
        System.out.print("H320发动。。。");
    }

    @Override
    public void engineBoom() {
        System.out.print("H320引擎发出轰鸣声 轰轰轰。。。");
    }

    @Override
    public void run() {
        this.start();
        this.engineBoom();
        System.out.print("跑起来了,");
        this.alarm();
        this.stop();
    }

    @Override
    public void alarm() {
        System.out.print("H320:妈的,嘀嘀嘀。。。好狗不挡道,快让开");
    }

    @Override
    public void stop() {
        System.out.println("H320:前方遇到紧急情况,滋滋滋...,总算停下来了。");
    }

}

2.2、H523悍马

package com.chb.l.TemplateDesignPattern;
/**
 230系列车型
 */
public class Hummer523 extends HummerModel{

    @Override
    public void start() {
        System.out.print("H523发动。。。");
    }

    @Override
    public void engineBoom() {
        System.out.print("H523引擎发出轰鸣声 轰轰轰。。。");
    }

    @Override
    public void run() {
        this.start();
        this.engineBoom();
        System.out.print("跑起来了,");
        this.alarm();
        this.stop();
    }

    @Override
    public void alarm() {
        System.out.print("H523:妈的,嘀嘀嘀。。。好狗不挡道,快让开");
    }

    @Override
    public void stop() {
        System.out.println("H523:前方遇到紧急情况,滋滋滋...,总算停下来了。");
    }

}

查看两种型号悍马,我们可以发现,两者具有一个共性方法run(), 那么这个共性方法应该出现在抽象类中,而不是实现类中,抽象是所有子类的共性封装

共性方法

    @Override
    public void run() {
        this.start();
        this.engineBoom();
        System.out.print("跑起来了,");
        this.alarm();
        this.stop();
    }

注意 在软件开发过程中,如果相同的一段代码复制过两次,就需要对设计产生怀疑, 架构师要明确地说明为什么相同的逻辑要出现两次或更多次,我们将共性方法抽取到抽象类中

package com.chb.l.TemplateDesignPattern;
/**
一辆车的功能
    start
    engineboom
    run
    alarm
    stop
 */
public abstract class HummerModel {
    /**
     * 启动
     */
    public abstract void start();
    /**
     * 引擎轰鸣
     */
    public abstract void engineBoom();
    /**
     * 行驶,共性方法抽取到抽象类,子类不用重新实现。
     */
    public void run() {
        this.start();
        this.engineBoom();
        System.out.print("跑起来了,");
        this.alarm();
        this.stop();
    }
    /**
     * 遇到情况鸣笛
     */
    public abstract void alarm();
    /**
     * 停车
     */
    public abstract void stop();


}

测试


public class Test {
    public static void main(String[] args) {
        HummerModel hm320 = new Hummer320();
        hm320.run();
    }
}



输出结果:
H320发动。。。H320引擎发出轰鸣声 轰轰轰。。。跑起来了,H320:妈的,嘀嘀嘀。。。好狗不挡道,快让开H320:前方遇到紧急情况,滋滋滋...,总算停下来了。

模板方法


这个共性方法run()就是模板方法,不管车子是什么型号, 一定是启动,引擎发动,可以跑了,遇到问题鸣笛,遇到紧急情况刹车。这个流程是不变的,即run()的实现不变,所以称为模板方法

  注意 为了防止恶意的操作,一般模板方法都加上final关键字,不允许被覆写。 抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为protected类型。实现类若非必要,尽量不要扩大父类中的访问权限。

模板方法模式的使用场景


● 多个子类有公有的方法,并且逻辑基本相同时。
● 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个 子类实现。
● 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通 过钩子函数(见“模板方法模式的扩展”)约束其行为。

三、模板方法模式的扩展

目前为止,这两个模型都稳定地运行,突然有一天,领导急匆匆地找到了我:“看你怎么设计的,车子一启动,喇叭就狂响,吵死人了!客户提出H1型号的悍马喇叭想让它响就响,H2型号的喇叭不要有声音,赶快修改一下。”

怎么做呢

整个流程不变, 只需改变可以控制鸣笛,添加一个控制因素来选择是否鸣笛
添加一个控制方法isAlarm()来控制是否鸣笛。

    /**
     * 钩子方法,默认是会鸣笛
     * @return
     */
    protected boolean isAlarm() {
        return true;
    } 

修改模板方法

    /**
     * 行驶
     */
    public final void run() {
        this.start();
        this.engineBoom();
        System.out.print("跑起来了,");
        if (isAlarm()) {//想叫它鸣笛就鸣笛
            this.alarm();
        }
        this.stop();
    }

抽象类

package com.chb.l.TemplateDesignPattern;
/**
一辆车的功能
    start
    engineboom
    run
    alarm
    stop
 */
public abstract class HummerModel {
    /**
     * 启动
     */
    protected abstract void start();
    /**
     * 引擎轰鸣
     */
    protected abstract void engineBoom();
    /**
     * 行驶
     */
    public final void run() {
        this.start();
        this.engineBoom();
        System.out.print("跑起来了,");
        if (isAlarm()) {//想叫它鸣笛就鸣笛
            this.alarm();
        }
        this.stop();
    }
    /**
     * 遇到情况鸣笛
     */
    protected abstract void alarm();
    /**
     * 停车
     */
    protected abstract void stop();
    /**
     * 钩子方法,默认是会鸣笛
     * @return
     */
    protected boolean isAlarm() {
        return true;
    } 
}

H320重构

package com.chb.l.TemplateDesignPattern;
/**
 230系列车型
 */
public class Hummer320 extends HummerModel{
    //设置一个变量供客户控制钩子方法
    private boolean alarmFlag = true;
    /**
     * 要不要响喇叭,是由客户来决定的
     * @param alarmFlag
     */
    public void setAlarmFlag(boolean alarmFlag) {
        this.alarmFlag = alarmFlag;
    }
    protected boolean isAlarm() {//将外部的参数,通过钩子方法传入公共方法run(),决定是否鸣笛。
        return this.alarmFlag;
    };

    @Override
    protected void start() {
        System.out.print("H320发动。。。");
    }

    @Override
    protected void engineBoom() {
        System.out.print("H320引擎发出轰鸣声 轰轰轰。。。");
    }



    @Override
    protected void alarm() {
        System.out.print("H320:妈的,嘀嘀嘀。。。好狗不挡道,快让开");
    }

    @Override
    protected void stop() {
        System.out.println("H320:前方遇到紧急情况,滋滋滋...,总算停下来了。");
    }

}

H320型号的悍马是由客户自己控制是否要响喇叭,也就是说外界条件改变,影响到模板方法的执行。在我们的抽象类中isAlarm的返回值就是影响了模板方法的执行结果,该方法就叫做钩子方法(Hook Method)。有了钩子方法模板方法模式才算完美,大家 可以想想,由子类的一个方法返回值决定公共部分的执行结果,是不是很有吸引力呀!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值