模板方法模式

Posted heliusking

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板方法模式相关的知识,希望对你有一定的参考价值。

假设我们现在要造一批悍马汽车,悍马汽车有两个系列H1和H2,首先不考虑任何设计模式,看看设计的类图:

技术图片

看看代码的实现:

/**
 * @description: 代表悍马汽车的抽象类
 */
public abstract class Hummer {
    
    public abstract void start(); // 启动汽车
    public abstract void stop(); // 刹车
    public abstract void alarm(); // 鸣笛
    public abstract void engineBoom(); // 启动引擎
    public abstract void run(); // 汽车行驶的整个过程
    
}
public class HummerH1 extends Hummer {

    @Override
    public void start() {
        System.out.println("悍马H1启动...");
    }

    @Override
    public void stop() {
        System.out.println("悍马H1停止...");
    }

    @Override
    public void alarm() {
        System.out.println("悍马H1鸣笛...");
    }

    @Override
    public void engineBoom() {
        System.out.println("悍马H1引擎轰鸣...");
    }

    /**
     * 悍马H1行驶的整个过程
     */
    @Override
    public void run() {
        start();
        engineBoom();
        alarm();
        stop();
    }

}
public class HummerH2 extends Hummer {
    
    @Override
    public void start() {
        System.out.println("悍马H2启动...");
    }

    @Override
    public void stop() {
        System.out.println("悍马H2停止...");
    }

    @Override
    public void alarm() {
        System.out.println("悍马H2鸣笛...");
    }

    @Override
    public void engineBoom() {
        System.out.println("悍马H2引擎轰鸣...");
    }

    /**
     * 悍马H2行驶的整个过程
     */
    @Override
    public void run() {
        start();
        engineBoom();
        alarm();
        stop();
    }
}

程序写到这里,我们发现,run()方法的实现应该在抽象类上,不应该在实现类上,好,我们修改一下类图和实现:

技术图片

public abstract class Hummer {
    
    public abstract void start(); // 启动汽车
    public abstract void stop(); // 刹车
    public abstract void alarm(); // 鸣笛
    public abstract void engineBoom(); // 启动引擎
    
    // 汽车行驶的过程
    public void run() {
        start();
        engineBoom();
        alarm();
        stop();
    } 
    
}
public class HummerH1 extends Hummer {

    @Override
    public void start() {
        System.out.println("悍马H1启动...");
    }

    @Override
    public void stop() {
        System.out.println("悍马H1停止...");
    }

    @Override
    public void alarm() {
        System.out.println("悍马H1鸣笛...");
    }

    @Override
    public void engineBoom() {
        System.out.println("悍马H1引擎轰鸣...");
    }

}
public class HummerH2 extends Hummer {
    
    @Override
    public void start() {
        System.out.println("悍马H2启动...");
    }

    @Override
    public void stop() {
        System.out.println("悍马H2停止...");
    }

    @Override
    public void alarm() {
        System.out.println("悍马H2鸣笛...");
    }

    @Override
    public void engineBoom() {
        System.out.println("悍马H2引擎轰鸣...");
    }

}

接下来我们测试一下模板方法模式:

public class App {
    
    public static void main(String[] args) {
        
        HummerH1 h1 = new HummerH1();
        h1.run();
        
        HummerH2 h2 = new HummerH2();
        h2.run();
        
    }

}

结果:

悍马H1启动...
悍马H1引擎轰鸣...
悍马H1鸣笛...
悍马H1停止...
悍马H2启动...
悍马H2引擎轰鸣...
悍马H2鸣笛...
悍马H2停止...

然后我们又发现问题了,悍马牌汽车start()、stop()、alarm()、engineBoom()方法是只有悍马系列的车(H1、H2)可以调用,奥拓是万万不能使用悍马的这些的方法的,那么我们就把抽象类的其他4个方法的权限改为protected

而且,run()方法既然子类都不修改,就设置为final类型的方法

修改后的类图和Hummer类的代码如下:

技术图片

public abstract class Hummer {
    
    protected abstract void start(); // 启动汽车
    protected abstract void stop(); // 刹车
    protected abstract void alarm(); // 鸣笛
    protected abstract void engineBoom(); // 启动引擎
    
    // 汽车行驶的过程
    public final void run() {
        start();
        engineBoom();
        alarm();
        stop();
    } 
    
}

其他的子类都不用修改(把子类的4个重写的方法都设置为protected也可以),大家请看这个run()方法,他定义了调用其他方法的顺序,并且子类是不能修改的,这个叫做模板方法

start()、stop()、alarm()、engineBoom()这四个方法是子类必须实现的,而且这四个方法的修改对应了不同的类,这个叫做基本方法,基本方法又分为三种:在抽象类中实现了的基本方法叫做具体方法;在抽象类中没有实现,在子类中实现了叫做抽象方法,我们这四个基本方法都是抽象方法,由子类来实现的;还有一种叫做钩子方法,看下面的内容来学习一下钩子方法:

我们发现,汽车一运行起来就会鸣笛(调用alarm()),更符合实际情况的是,我想让它鸣笛它才能鸣笛,我不按喇叭它就不能自己响,于是增加一个钩子方法,isAlarm(),继续修改类图:

技术图片

修改Hummer类:

public abstract class Hummer {
    
    protected abstract void start(); // 启动汽车
    protected abstract void stop(); // 刹车
    protected abstract void alarm(); // 鸣笛
    protected abstract void engineBoom(); // 启动引擎
    
    // 钩子方法,默认喇叭是会响的
    protected boolean isAlarm() { 
        return true; 
    }
    
    // 汽车行驶的过程
    public final void run() {
        start();
        engineBoom();
        if(isAlarm()) {
            alarm();
        }
        stop();
    } 
    
}

H2系列的悍马车不会鸣笛,喇叭就是个摆设,那么代码如下:

public class HummerH2 extends Hummer {
    
    @Override
    public void start() {
        System.out.println("悍马H2启动...");
    }

    @Override
    public void stop() {
        System.out.println("悍马H2停止...");
    }

    @Override
    public void alarm() {
        System.out.println("悍马H2鸣笛...");
    }

    @Override
    public void engineBoom() {
        System.out.println("悍马H2引擎轰鸣...");
    }
    
    @Override
    public boolean isAlarm() {
        return false;
    }

}

H1系列的悍马,用户可以自己决定是否鸣笛,代码如下:

public class HummerH1 extends Hummer {
    
    private boolean alarmFlag = true; //是否要响喇叭
    
    //要不要响喇叭,是由用户来决定的
    public void setAlarm(boolean isAlarm){ 
        this.alarmFlag = isAlarm; 
    }

    @Override
    public void start() {
        System.out.println("悍马H1启动...");
    }

    @Override
    public void stop() {
        System.out.println("悍马H1停止...");
    }

    @Override
    public void alarm() {
        System.out.println("悍马H1鸣笛...");
    }

    @Override
    public void engineBoom() {
        System.out.println("悍马H1引擎轰鸣...");
    }
    
    //钩子方法,默认喇叭是会响的
    @Override
    protected boolean isAlarm(){ 
        return this.alarmFlag; 
    }

}

然后我们修改一下测试代码:

public class App {
    
    public static void main(String[] args) {
        
        HummerH1 h1 = new HummerH1();
        // 我就不想听见H1的喇叭响!
        h1.setAlarm(false);
        h1.run();
        
        HummerH2 h2 = new HummerH2();
        h2.run();
        
    }

}

结果:

悍马H1启动...
悍马H1引擎轰鸣...
悍马H1停止...
悍马H2启动...
悍马H2引擎轰鸣...
悍马H2停止...

总结一下模板方法模式:

模板方法模式就是在模板方法中按照一个的规则和顺序调用基本方法,具体到我们上面那个例子就是run()方法按照规定的顺序(先调用start(),然后再调用engineBoom(),再调用alarm(),最后调用stop())调用本类的其他方法,并且由isAlarm()方法的返回值确定run()中的执行顺序变更,通用类图如下:

技术图片

其中templateMethod()就是模板方法,operation1()和operation2()就是基本方法,模板方法是通过汇总或排序基本方法而产生的结果集。

原文链接:https://www.jianshu.com/p/8ad6c21370b6

以上是关于模板方法模式的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段1——vue主模板

VSCode自定义代码片段2——.vue文件的模板

VSCode自定义代码片段(vue主模板)

VS Code配置snippets代码片段快速生成html模板,提高前端编写效率

设计模式模板方法模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )