在模板方法模式中有一个抽象的类,该抽象类定义了相关方法的模板,它的子类可以根据需要重写模板中的方法,但调用还是会以在抽象类中定义好的方式进行。

概述

实现一些操作时,整体步骤很固定,但是呢。就是其中一小部分需要改变,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。

优点:

  • 不变的部分已封装好,剩余可变的部分可随意扩展。
  • 行为延迟到子类实现,但控制权在父类手中。
  • 提取出了公共代码,维护变得简单。

缺点:

  • 每个不同的实现都需要一个子类,会导致类的个数增加,使得系统更加庞大。
  • 通过继承的方式实现,而不是组合。

使用场景:

  • 多个子类共有一些方法,且逻辑相同时。
  • 代码逻辑重要且复杂,可以将核心方法抽象出来设计为模板,且其它相关的方法可延迟到子类中实现时。
  • 重构时模板方法模式也是一种常见的处理手段,可以把相同的代码抽取到父类中,然后通过钩子函数约束其行为。

    如果想防止不规范操作,可在模板方法前面加上 final 关键字。

示例

去餐厅吃饭,餐厅给我们提供了一个模板就是:看菜单,点菜,吃饭,付款,走人 (这里 “点菜和付款” 是不确定的由子类来完成的,其他的则是一个模板。)

  1. 先定义一个模板。把模板中的点菜和付款,让子类来实现。

    1. //模板方法
    2. public abstract class RestaurantTemplate {
    3. // 1.看菜单
    4. public void menu() {
    5. System.out.println("看菜单");
    6. }
    7. // 2.点菜业务
    8. abstract void spotMenu();
    9. // 3.吃饭业务
    10. public void havingDinner(){ System.out.println("吃饭"); }
    11. // 3.付款业务
    12. abstract void payment();
    13. // 3.走人
    14. public void GoR() { System.out.println("走人"); }
    15. //模板通用结构
    16. public void process(){
    17. menu();
    18. spotMenu();
    19. havingDinner();
    20. payment();
    21. GoR();
    22. }
    23. }
  2. 具体的模板方法子类1

    1. public class RestaurantGinsengImpl extends RestaurantTemplate {
    2. void spotMenu() {
    3. System.out.println("人参");
    4. }
    5. void payment() {
    6. System.out.println("5快");
    7. }
    8. }
  3. 具体的模板方法子类2

    1. public class RestaurantLobsterImpl extends RestaurantTemplate {
    2. void spotMenu() {
    3. System.out.println("龙虾");
    4. }
    5. void payment() {
    6. System.out.println("50块");
    7. }
    8. }
  4. 客户端测试

    1. public class Client {
    2. public static void main(String[] args) {
    3. //调用第一个模板实例
    4. RestaurantTemplate restaurantTemplate = new RestaurantGinsengImpl();
    5. restaurantTemplate.process();
    6. }
    7. }

    总结

    模板方法实际上可以理解成一个封装好了的固定流程,第一步该做什么,第二步该做什么其实都已经在抽象类中定义好了。子类可以有不同的算法实现,可以在框架不被修改的情况下实现某些步骤的算法替换。