模版模式在项目中的应用

在常见的23种设计模式中模板模式属于三大类中的行为模式,也是我们在日常开发中比较常见的设计模式,其主要含义是定义抽象类并且声明一些抽象基本方法供子类实现不同逻辑,同时在抽象类中定义具体方法把抽象基本方法封装起来,这就是模板方法模式。
模板方法

如上图:父类中具有两种方法,相当于定义一个框架。子类通过复写父类的方法,调用父类的模板方法就是模板执行方法来完成具体的业务流程。

  • 模板模式中的角色
  1. 抽象的模板定义角色:定义一组基本方法供子类实现,定义并实现组合了基本方法的模板方法。
  2. 具体模板子类角色:实现抽象模板角色定义的基本方法。
  • 模板模式中的具体方法
  1. 抽象方法:由抽象模板角色声明,abstract修饰,具体模板角色实现。
  2. 具体方法:由抽象模板角色声明并实现,而子类并不实现。一般以final修饰,不允许具体子类模板角色重写。
  3. 钩子方法(如果需要):由抽象模板角色声明并实现,具体模板角色可实现加以扩展。

代码示例实战

我们来举一个具体生活中的例子,来用代码说明一下模板模式是怎么具体体现的。我们描述一个去医院看病的场景,一般我们去医院看病的流程是固定的,需要先去挂号,之后拿着挂号单去看医生,之后去进行各项检查,之后再去看医生,最后进行对应治疗。这就是一个典型的模板方法流程,抽象父类就是这个流程,各个人也就是子类来看病都要走这个流程。接下来我们用代码来实现一下:

  1. 我们先定义一个抽象的流程并进行具体方法调用的定义
public abstract class AbstractHospital {

    /**
     * 挂号
     */
    protected abstract void register();

    /**
     * 看医生
     */
    protected abstract void seeDoc();

    /**
     * 各项检查
     */
    protected abstract void checkBody();

    /**
     * 治疗
     */
    protected abstract void verb();

    /**
     * 去医院看病
     */
    public final void work() {
        register();
        seeDoc();
        checkBody();
        verb();
    }
}
复制代码
  1. 我们接着构建2个实例继承上面的抽象流程
public class Person1Hospital extends AbstractHospital{
    @Override
    protected void register() {
        System.out.println("我是第一个人来挂号");
    }

    @Override
    protected void seeDoc() {
        System.out.println("我是第一个人在看医生");
    }

    @Override
    protected void checkBody() {
        System.out.println("我是第一个人在进行身体检查");
    }

    @Override
    protected void verb() {
        System.out.println("我是第一个人开始治疗了");
    }

}
复制代码
public class Person2Hospital extends AbstractHospital{
    @Override
    protected void register() {
        System.out.println("我是第二个人来挂号");
    }

    @Override
    protected void seeDoc() {
        System.out.println("我是第二个人在看医生");
    }

    @Override
    protected void checkBody() {
        System.out.println("我是第二个人在进行身体检查");
    }

    @Override
    protected void verb() {
        System.out.println("我是第二个人开始治疗了");
    }
}
复制代码
  1. 编写一个客户端进行调用
public class Client {
    public static void main(String[] args) {
        Person1Hospital person1Hospital = new Person1Hospital();
        person1Hospital.work();

        System.out.println("-----------");

        Person2Hospital person2Hospital = new Person2Hospital();
        person2Hospital.work();
    }
}
复制代码

输出结果:

我是第一个人来挂号

我是第一个人在看医生
我是第一个人在进行身体检查
我是第一个人开始治疗了
———–
我是第二个人来挂号
我是第二个人在看医生
我是第二个人在进行身体检查
我是第二个人开始治疗了

上面代码的含义就是构架2个去医院看医生的人,无论你是什么病都需要按照上边的流程进行,所以在父类定义一个算法来控制所有子类的流程。具体实现交给具体子类,再由子类调用父类的work(模板方法)进行运作。

模板模式钩子方法使用

就那上边的案例来讲,有的人来到医院看完医生之后医生就嘱咐他回家多喝水不需要吃药和治疗,对于这些病人我们就没有必要再去安装统一的流程去实现了。那如何去控制呢?我们可以通过钩子函数约束其行为。

  1. 重新定义抽象模板类
public abstract class AbstractHospital2 {

    /**
     * 挂号
     */
    protected abstract void register();

    /**
     * 看医生
     */
    protected abstract void seeDoc();

    /**
     * 各项检查
     */
    protected abstract void checkBody();

    /**
     * 治疗
     */
    protected abstract void verb();

    //钩子函数
    protected boolean checkNeedVerb(){
        return true;
    }

    /**
     * 去医院看病
     */
    public final void work() {
        register();
        seeDoc();
        checkBody();
        if (checkNeedVerb()){

            verb();
        }
    }
}
复制代码
  1. 定义第三个人去医院看病
public class Person3Hospital extends AbstractHospital2{
    @Override
    protected void register() {
        System.out.println("我是第3个人来挂号");
    }

    @Override
    protected void seeDoc() {
        System.out.println("我是第3个人在看医生");
    }

    @Override
    protected void checkBody() {
        System.out.println("我是第3个人在进行身体检查");
    }

    @Override
    protected void verb() {
        System.out.println("我是第3个人开始治疗了");
    }

    @Override
    protected boolean checkNeedVerb() {
        return super.checkNeedVerb();
    }

    protected void setNeedVerb(boolean b){
        super.checkNeedVerb = b;
    }

}
复制代码
  1. 客户端调用
Person3Hospital person3Hospital = new Person3Hospital();
person3Hospital.setNeedVerb(false);
person3Hospital.work();
复制代码

输出结果:

我是第3个人来挂号

我是第3个人在看医生
我是第3个人在进行身体检查

通过钩子函数给不需要治疗的病人进行灵活调整。其实钩子函数就是子类方法决定模板方法的执行结果,这样就灵活了许多。

模板方法模式就是在模板方法中按照一定的规则和顺序调用基本方法,具体到前面的例子就是先调用register(),再调用seeDoc(),然后调用checkBody()选择是否执行verb()。 按照我们的设计习惯抽象类负责定义,实现类负责实现具体逻辑,而模板方法确实抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响到了父类的结果,所以其最大的缺点就是增大了阅读代码的难度。其最大的优点就是封装了不变部分,扩展了可变部分,提取公共代码,利于维护。

模板方法模式的优点

  • 实现了最大化代码复用,父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
  • 父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现
  • 良好的扩展性:增加功能由子类实现基本方法扩展,符合单一职责原则和开闭原则。

模板方法模式实际应用

当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理。
我们使用spring框架时,其实就应用到了模板方法模式。springIOC容器初始化时每一步需要执行哪些方法都有父类定义了,交给子类去实现。还有我们经常用的JdbcTemplate也大量使用了这个设计模式,我们拿spring先看一下,springioc中最核心的refresh方法相信大家都了解,我们来看一下:
iShot2021-06-0116
如图标注的一个抽象方法obtainFreshBeanFactory、两个钩子方法postProcessBeanFactory和onRefresh。
我们看obtainFreshBeanFactory这个方法:

iShot2021-06-0116

也就是refresh方法中会通过obtainFreshBeanFactory();方法调用2个抽象方法。具体抽象方法实现放到子类中。对应2个子类是:
iShot2021-06-0116

如果大家还不理解我们来画个图跟我们写的例子一一对应上就明白了:
iShot2021-06-0116
看这个图,相同颜色的代办同一个含义。这样就非常好理解了。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享