Java设计模式之模板方法模式

假设我们现在要造一批悍马汽车,悍马汽车有两个系列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();
    } 
    
}

其他的子类都不用修改,大家请看这个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()就是基本方法,模板方法是通过汇总或排序基本方法而产生的结果集。

好了,模板方法模式就是这样,以后再有高手很牛X的说“用模板方法模式就可以实现…”,你再也不用很崇拜的看着他,哇,牛人,模板方法模式是什么呀?

本文原书: 《您的设计模式》 作者:CBF4LIFE

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一英里广度一英寸深度的学习

Spring 基础

interface声明接口,接口中的函数不用public/private,但是要匹配参数和异常。

961
来自专栏Coding迪斯尼

VUE+WebPack游戏设计:实现子弹发射击打外星人效果

1023
来自专栏黑泽君的专栏

day34_Spring学习笔记_02

1.2.2.2、切面类   MyAspect.java的代码同上 1.2.1.2、切面类 代码,这里不再赘述!

894
来自专栏JMCui

再学习之Spring(面向切面编程).

一、概念 1、理论     把横切关注点和业务逻辑相分离是面向切面编程所要解决的问题。如果要重用通用功能的话,最常见的面向对象技术是继承(inheritance...

2615
来自专栏Hongten

spring开发_Annotation_AOP_Before增强处理

http://www.cnblogs.com/hongten/gallery/image/112691.html

972
来自专栏xingoo, 一个梦想做发明家的程序员

【Spring实战】—— 9 AOP环绕通知

假如有这么一个场景,需要统计某个方法执行的时间,如何做呢?   典型的会想到在方法执行前记录时间,方法执行后再次记录,得出运行的时间。 如果采用Sp...

2305
来自专栏javathings

Spring 中 AOP 的实现

AOP 称为面向切面编程,在程序开发中,AOP 技术可以在不改变原有方法代码的情况下,把逻辑直接插入到方法上。Spring AOP 的大致原理主要依靠的是动态代...

1094
来自专栏编程

java框架,springAOP-顾问的实现必须要把通知功能封装起来!

好的各位小伙伴,上次分享的【springAOP—实现通知系统的全过程】大家学会了吗? 如果学会了那么我们今天就继续分享! 好了又到了共享知识的时刻了 准备好了吗...

19110
来自专栏大内老A

.NET Core采用的全新配置系统[9]: 为什么针对XML的支持不够好?如何改进?

物理文件是我们最常用到的原始配置的载体,最佳的配置文件格式主要由三种,它们分别是JSON、XML和INI,对应的配置源类型分别是JsonConfiguratio...

1925
来自专栏Kiba518

【我们一起写框架】C#的AOP框架

因为,AOP单独设计的框架几乎是无法使用的。普遍的情况是,AOP要是和其他设计模式结合在一起使用。

1153

扫码关注云+社区

领取腾讯云代金券