设计模式---模板设计模式(java)

模板设计模式

1)基本定义

定义:在一个抽象类中公开定义执行它的方法的方式/模板,子类可以重写方法的实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型设计模式。
意图:定义一个操作种的算法骨架,将一些具体步骤的实现延迟到子类中
解决问题:一些方法通用,却在每一个子类重写这些方法
关键代码:算法骨架在抽象类实现(有时使用final修饰以禁止子类重写以防止恶意修改),一些具体步骤在子类类实现
优点:封装不变部分、扩展可变部分;提取公共代码、便于维护;行为由父类统一控制,子类实现
缺点:每一个不同的实现都需要一个子类实现,导致类数量增加,系统变得庞大
场景:有多个子类共有的方法,逻辑相同;重要的、复杂的方法,可以考虑作为模板方法

设计模式类图

 2)例子

以悍马车模型为例,类图如下:

实现代码如下:

/** 悍马车模型 **/
public abstract class HummerModel {
    /** 启动 **/
    public abstract void start();
    /** 停止 **/
    public abstract void stop();
    /** 喇叭鸣叫 **/
    public abstract void alarm();
    /** 引擎轰鸣 **/
    public abstract void engineBoom();
    /** 汽车跑起来 **/
    public abstract void run();
}

public class HM1 extends HummerModel{
    @Override
    public void start() {
        System.out.println("HM1 start");
    }
    @Override
    public void stop() {
        System.out.println("HM1 stop");
    }
    @Override
    public void alarm() {
        System.out.println("HM1 alarm");
    }
    @Override
    public void engineBoom() {
        System.out.println("HM1 engineBoom");
    }
    @Override
    public void run() {
        System.out.println("HM1 run");
        alarm();
        start();
        engineBoom();
        stop();
    }
}

public class HM2 extends HummerModel{
    @Override
    public void start() {
        System.out.println("HM2 start");
    }
    @Override
    public void stop() {
        System.out.println("HM2 stop");
    }
    @Override
    public void alarm() {
        System.out.println("HM2 alarm");
    }
    @Override
    public void engineBoom() {
        System.out.println("HM2 engineBoom");
    }
    @Override
    public void run() {
        System.out.println("HM2 run");
        alarm();
        start();
        engineBoom();
        stop();
    }
}

观察上面代码,发现明显的问题,让汽车跑起来的方法run()所有的车型应该是一样的,应该在抽象类中,修改类图如下:

新的实现代码如下:

/** 悍马车模型 **/
public abstract class HummerModel {
    /** 启动 **/
    public abstract void start();
    /** 停止 **/
    public abstract void stop();
    /** 喇叭鸣叫 **/
    public abstract void alarm();
    /** 引擎轰鸣 **/
    public abstract void engineBoom();
    /** 汽车跑起来 **/
    public final void run() {
        System.out.println("HM2 run");
        alarm();
        start();
        engineBoom();
        stop();
    }
}

public class HM1 extends HummerModel{
    @Override
    public void start() {
        System.out.println("HM1 start");
    }
    @Override
    public void stop() {
        System.out.println("HM1 stop");
    }
    @Override
    public void alarm() {
        System.out.println("HM1 alarm");
    }
    @Override
    public void engineBoom() {
        System.out.println("HM1 engineBoom");
    }
}

public class HM2 extends HummerModel{
    @Override
    public void start() {
        System.out.println("HM2 start");
    }
    @Override
    public void stop() {
        System.out.println("HM2 stop");
    }
    @Override
    public void alarm() {
        System.out.println("HM2 alarm");
    }
    @Override
    public void engineBoom() {
        System.out.println("HM2 engineBoom");
    }
}

调用如下:

public class HMTest {
    public static void main(String[] args) {
        HummerModel h1 = new HM1();
        h1.run();
        HummerModel h2 = new HM2();
        h2.run();
    }
}

仔细思考上面的实现,会发现以下问题:
1)客户要关心模型的启动,停止,鸣笛,引擎声音吗?他只要在run的过程中,听到或看到就成了,暴露那么多方法干嘛呢?把抽象类上的四个方法设置为protected访问权限
2)run方法设置成final,子类不可修改,但不是所有的车型都是想鸣喇叭,且客户更想在想要鸣时就鸣,由自己决定,这就可以使用钩子方法。
修改类图如下:

实现代码如下:

/** 悍马车模型 **/
public abstract class HummerModel {
    /** 启动 **/
    protected abstract void start();
    /** 停止 **/
    protected abstract void stop();
    /** 喇叭鸣叫 **/
    protected abstract void alarm();
    /** 引擎轰鸣 **/
    protected abstract void engineBoom();
    /** 汽车跑起来 **/
    public final void run() {
        System.out.println("HM2 run");
        if(isAlarm())
            alarm();
        start();
        engineBoom();
        stop();
    }
    /** 钩子方法,是否鸣笛 ,默认实现**/
    protected boolean isAlarm() {
        return true;
    }
}

public class HM1 extends HummerModel{
    @Override
    protected void start() {
        System.out.println("HM1 start");
    }
    @Override
    protected void stop() {
        System.out.println("HM1 stop");
    }
    @Override
    protected void alarm() {
        System.out.println("HM1 alarm");
    }
    @Override
    protected void engineBoom() {
        System.out.println("HM1 engineBoom");
    }
    
    private boolean isAlarm = false;
    /** 子类自己提供设置是否鸣笛的接口 **/
    public void setIsAlarm(boolean isAlarm) {
        this.isAlarm = isAlarm;
    }
    /** 重写钩子方法,是否鸣笛 **/
    protected boolean isAlarm() {
        return isAlarm;
    }
}

public class HM2 extends HummerModel{
    @Override
    protected void start() {
        System.out.println("HM2 start");
    }
    @Override
    protected void stop() {
        System.out.println("HM2 stop");
    }
    @Override
    protected void alarm() {
        System.out.println("HM2 alarm");
    }
    @Override
    protected void engineBoom() {
        System.out.println("HM2 engineBoom");
    }
    /** 不提供钩子方法重写,不提供设置是否鸣笛的接口 **/
}

调用如下:

public class HMTest {
    public static void main(String[] args) {
        HM1 h1 = new HM1();
        h1.setIsAlarm(true);//这里setIsAlarm方法不在父类中,只可以使用子类对象调用
        h1.run();
        HummerModel h2 = new HM2();
        h2.run();
    }
}

注意:callback和“钩子”是两个完全不同的概念,callback是指:由我们自己实现的,但是预留给系统调用的函数,我们自己是没有机会调用的,但是我们知道系统在什么情况下会调用该方法。而“钩子”是指:声明在抽象类中的方法,只有空的或默认的实现,通常应用在模板设计模式中,让子类可以对算法的不同点进行选择或挂钩,要不要挂钩由子类决定。

由例子可以看出,模板设计核心原理是通过继承、重写、实现来实现父类调用子类方法。

原文地址:https://www.cnblogs.com/ShouWangYiXin/p/10862408.html