「设计模式」模板模式

1,613 阅读3分钟

一、概述

模板方法模式(Template Pattern)定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法模式可能是我们常用但又不自知的设计模式了,在我们开发过程中经常会遇到,并且也非常简单,只不过,很多时候并不知道它是模板方法模式而已。

主要角色

  • AbstractClass:抽象类,类中实现类模版方法(template),定义了算法但骨架,具体子类需要去实现其他的抽象方法。
  • ConcreteClass :具体子类,完成算法中特定子类的步骤。

基本方法

  • 抽象方法:由抽象模板角色声明,abstract修饰,具体模板角色实现。
  • 钩子方法:由抽象模板角色声明并实现,具体模板角色可实现加以扩展。(比如决定某些步骤是否需要执行)
  • 具体方法:由抽象模板角色声明并实现,而子类并不实现。
  • 模板方法:由抽象模板角色声明并实现,负责对基本方法的调度,一般以final修饰,不允许具体模板角色重写。模板方法一般也是一个具体方法。

从定义上看,模板方法模式和建造者模式有些类似,但是区别是什么呢?

先回顾一下建造者模式:东小西:「设计模式」建造者模式

(1)模板方法的组装在父类,构建者模式的组装在director。

(2)模板方法模式已经定义好了建造者的算法的工序。

(3)建造者模式没有定义建造者的工序,而是交给子类去实现建造者的算法的工序。

二、优缺点

优点

  1. 封装不变部分,扩展可变部分。
  2. 提取公共代码,便于维护。
  3. 行为由父类控制,子类实现。

缺点

每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

三、实现方式

下面依然结合建造者模式中的使用案例(Datax)来了解一下模板方法模式的实现方式。(在这里大家了不了解Datax不重要)在使用Datax的时候需要生成一个JSON配置文件,具体格式如下:

在生成这个JSON配置文件时,主要的需要readerwritersetting三个核心配置构造流程,最终生成一个完整的配置文件。

我们使用Datax,是用来将 Mysql/Oracle 数据导出到 Hive 数据库中,对于导出同一个表来说,生成的mysqlToHive.jsonoracleToHive.jsonwriter 是相同的,readersetting 是不同的,oracleToHive.json可能还需要一些额外的配置项(otherSetting)。

抽象模板类

public abstract class AbstactClass {

    /**
     * 抽象方法,子类需要实现
     */
    protected abstract void reader();

    /**
     * 具体方法,子类可覆盖
     */
    protected void writer(){
        System.out.println("create hive-writer");
    }

    /**
     * 抽象方法,子类需要实现
     */
    protected abstract void setting();

    /**
     * 抽象方法,子类需要实现
     */
    protected abstract void otherSetting();

    /**
     * 钩子方法,子类可实现
     *
     */
    protected boolean needOtherSettingHook(){
        return true;
    }

    /**
     * 模板方法,负责调度基本方法,子类不可实现
     */
    public final void create() {
        reader();
        writer();
        setting();
        if(needOtherSettingHook()){
            otherSetting();
        }
    }

}

具体子类 MysqlConcreteClass

public class MysqlConcreteClass extends AbstactClass{
    @Override
    protected void reader() {
        System.out.println("create mysql-reader");
    }

    @Override
    protected void setting() {
        System.out.println("create mysql-setting");
    }

    @Override
    protected void otherSetting() {
    }

    @Override
    protected boolean needOtherSettingHook() {
        return false;
    }
}

具体子类 OracleConcreteClass

public class OracleConcreteClass extends AbstactClass{
    @Override
    protected void reader() {
        System.out.println("create oracle-reader");
    }

    @Override
    protected void setting() {
        System.out.println("create oracle-setting");
    }

    @Override
    protected void otherSetting() {
        System.out.println("create other-setting");
    }

    @Override
    protected boolean needOtherSettingHook() {
        return true;
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        System.out.println("begin create mysqlToHive.json");
        AbstactClass mysqlConcreteClass = new MysqlConcreteClass();
        mysqlConcreteClass.create();
        System.out.println("--------------------------");
        System.out.println("begin create oracleToHive.json");
        AbstactClass oracleConcreteClass= new OracleConcreteClass();
        oracleConcreteClass.create();
    }
}

结果输出

begin create mysqlToHive.json
create mysql-reader
create hive-writer
create mysql-setting
--------------------------
begin create oracleToHive.json
create oracle-reader
create hive-writer
create oracle-setting
create other-setting

四、常见应用场景

  • 有多个子类共有的方法,且逻辑相同。
  • 重要的、复杂的方法,可以考虑作为模板方法。