掘金 后端 ( ) • 2024-04-15 10:59

模板方法模式

  • 模板方法模式又叫模板模式(Template Pattern),属于行为型设计模式。
  • 它在一个抽象类中公开定义了需要执行的方法的模板,它的子类可以按需要重写细节方法,但调用将以抽象类中定义的方式进行。

进一步阐述: 模板方法模式是定义了一个逻辑算法的骨架,而将一些具体的变化的步骤延迟到子类中去实现,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。

模板方法模式包含二种角色:

  • AbstractClass(抽象角色):是一个抽象类,其内部定义了模板方法,也就是定义了整个算法的骨架和组成这个算法的各个步骤(这些步骤有具体的也有抽象的),它具体负责指挥整个算法的流程。
  • ConcreteClass(具体角色):继承了抽象角色,去重写步骤中变化部分的抽象方法。

案例:以泡功夫茶为例,泡功夫茶必须按照严格的步骤进行一步一步去做,最后喝到的才能是清香沁人上等茶品,其具体步骤是:沸水烫壶-->加入茶叶-->头茶洗茶-->养护茶宠-->首道闻香-->泡茶品茶

UML类图:

模板方法模式.jpg

Client客户端类:

/**
 * 客户端
 */
public class Client {
​
    public static void main(String[] args) {
        //泡龙井茶
        System.out.println("----------泡龙井茶----------------");
        KungFuTea longjingTea = new LongJingTea();
        longjingTea.makeTea();
​
        //泡铁观音茶
        System.out.println("----------泡铁观音茶----------------");
        KungFuTea tieGuanYinTea = new TieGuanYinTea();
        tieGuanYinTea.makeTea();
​
        //泡碧螺春茶
        System.out.println("----------泡碧螺春茶----------------");
        KungFuTea biLuoChunTea = new BiLuoChunTea();
        biLuoChunTea.makeTea();
    }
}

泡功夫茶的KungFuTea抽象类:

/**
 * 泡功夫茶的抽象类
 */
public abstract class KungFuTea {
​
    /**
     * 模板方法:用于定义整个算法的骨架,确定好整个算法的步骤顺序
     */
    public void makeTea() {
        wash();
        addTeaLeaves();
        makeFirst();
        maintainTeaUtil();
        smellTea();
        drinkTea();
    }
​
    /**
     * 1、沸水烫壶:“钩子”方法,默认空实现
     */
    protected void wash() {
​
    }
​
    /**
     * 2、加入茶叶
     */
    public abstract void addTeaLeaves();
​
    /**
     * 3、头茶洗茶
     */
    public void makeFirst() {
        System.out.println("加入第一次冲泡的热水,冲泡5-15秒,然后将茶倒入滤杯中。");
    }
​
    /**
     * 4、养护茶宠
     */
    public void maintainTeaUtil() {
        System.out.println("将第一次冲泡滤出来的茶水冲洗茶具。");
    }
​
    /**
     * 5、首道闻香
     */
    public void smellTea() {
        System.out.println("将首道茶水倒入闻香杯,捧杯闻香。");
    }
​
    /**
     * 6、泡茶品茶
     */
    public void drinkTea() {
        System.out.println("将沸水倒入紫砂壶,加盖泡制20秒,倒出后开始品尝。");
    }
}

泡龙井功夫茶LongJingTea类:

/**
 * 泡龙井功夫茶
 */
public class LongJingTea extends KungFuTea {
    @Override
    public void wash() {
        System.out.println("用预热的泉水加热和冲洗宜兴紫砂茶壶(或盖碗)");
    }
​
    @Override
    public void addTeaLeaves() {
        System.out.println("加入龙井茶茶叶。");
    }
}

泡铁观音功夫茶TieGuanYinTea类:

/**
 * 泡铁观音功夫茶
 */
public class TieGuanYinTea extends KungFuTea {
    @Override
    public void wash() {
        System.out.println("用预热的泉水加热和冲洗宜兴紫砂茶壶(或盖碗)");
    }
​
    @Override
    public void addTeaLeaves() {
        System.out.println("加入铁观音茶茶叶。");
    }
}

泡碧螺春功夫茶BiLuoChunTea类:

/**
 * 碧螺春功夫茶类
 */
public class BiLuoChunTea extends KungFuTea {
​
    @Override
    public void addTeaLeaves() {
        System.out.println("加入碧螺春茶茶叶。");
    }
}

总结:

  • 模板方法模式是将一个通用的算法抽离出来存放于一个公用的地方,也就是在父类中,提高代码的可复用性。如果整个算法的骨架需要变更只需要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改。
  • 它既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供变化步骤的实现。
  • 一般模板方法都加上会final关键字,防止子类重写模板方法去破坏掉整个算法的骨架体系。
  • 将一些可选步骤在抽象角色中设计为空实现即“钩子”方法,一般是用protected去修饰,方便子类去选择是否选用。
  • 每一个不同的细节都需要一个子类实现,导致类的个数增加从而增大系统类的复杂度。

典型运用场景举例: 当实现一个逻辑时,整体步骤是很固定的,但是某些细节步骤是具有很多变化的可能的,那么就可以使用模板方法模式进行设计。例如:jdbc访问数据库、操作各种非关系型数据库。