设计模式-模板方法模式

要理解设计模式中的模板方法模式,我们可以从其核心思想入手:定义一个算法的骨架(模板),将算法中某些步骤的具体实现延迟到子类中,使得子类可以在不改变算法整体结构的情况下,重写特定步骤的实现。它的本质是“固定流程、灵活细节”,通过抽象父类把控整体逻辑,子类专注于个性化实现。

一、模板方法模式的核心角色

模板方法模式包含2个核心角色,各自职责如下:

角色 职责描述
抽象类(Abstract Class) 定义算法的骨架(模板方法),包含一系列步骤方法(如抽象方法、具体方法、钩子方法)。模板方法通常用final修饰,防止子类修改算法结构。
具体子类(Concrete Class) 继承抽象类,实现抽象类中声明的抽象方法(即算法中需要个性化的步骤),也可选择重写钩子方法(用于控制算法流程)。

二、Java代码演示

我们以“制作热饮(泡茶和泡咖啡)”为例(两者流程相似:烧开水→冲泡→倒杯子→加调料,但“冲泡”和“加调料”的具体方式不同),演示模板方法模式的实现。

1. 定义抽象类(Abstract Class)

封装热饮制作的通用流程(模板方法),并声明需要子类实现的抽象步骤:

// 抽象类:热饮制作器
public abstract class HotDrinkMaker {

    // 模板方法:定义制作热饮的算法骨架(用final修饰,防止子类修改流程)
    public final void makeDrink() {
        boilWater(); // 步骤1:烧开水(通用步骤)
        brew(); // 步骤2:冲泡(子类实现)
        pourInCup(); // 步骤3:倒入杯子(通用步骤)
        // 钩子方法:判断是否需要加调料(默认需要,子类可重写)
        if (wantCondiments()) {
            addCondiments(); // 步骤4:加调料(子类实现)
        }
        System.out.println("热饮制作完成!\n");
    }

    // 具体方法:烧开水(所有热饮通用,父类实现)
    private void boilWater() {
        System.out.println("步骤1:烧开水(100℃)");
    }

    // 具体方法:倒入杯子(所有热饮通用,父类实现)
    private void pourInCup() {
        System.out.println("步骤3:将热饮倒入杯子");
    }

    // 抽象方法:冲泡(子类必须实现,不同热饮方式不同)
    protected abstract void brew();

    // 抽象方法:加调料(子类必须实现,不同热饮调料不同)
    protected abstract void addCondiments();

    // 钩子方法:是否需要加调料(默认返回true,子类可重写以改变流程)
    protected boolean wantCondiments() {
        return true;
    }
}

2. 定义具体子类(Concrete Class)

实现抽象类中的抽象方法,根据自身特性定制“冲泡”和“加调料”步骤,可选重写钩子方法:

// 具体子类1:泡茶
public class TeaMaker extends HotDrinkMaker {

    @Override
    protected void brew() {
        System.out.println("步骤2:用沸水浸泡茶叶");
    }

    @Override
    protected void addCondiments() {
        System.out.println("步骤4:加入柠檬片");
    }

    // 可选:重写钩子方法(例如:有些人喝茶不加调料)
    @Override
    protected boolean wantCondiments() {
        // 这里简化逻辑,固定返回true(实际可根据用户输入判断)
        return true;
    }
}

// 具体子类2:泡咖啡
public class CoffeeMaker extends HotDrinkMaker {

    @Override
    protected void brew() {
        System.out.println("步骤2:用沸水冲泡咖啡粉");
    }

    @Override
    protected void addCondiments() {
        System.out.println("步骤4:加入牛奶和糖");
    }

    // 不重写钩子方法,使用父类默认实现(加调料)
}

// 具体子类3:黑咖啡(不加调料,重写钩子方法)
public class BlackCoffeeMaker extends HotDrinkMaker {

    @Override
    protected void brew() {
        System.out.println("步骤2:用沸水冲泡黑咖啡粉");
    }

    @Override
    protected void addCondiments() {
        // 即使实现了该方法,由于钩子方法返回false,也不会被调用
        System.out.println("步骤4:加入...(但不会执行)");
    }

    // 重写钩子方法:黑咖啡不加调料
    @Override
    protected boolean wantCondiments() {
        return false;
    }
}

3. 客户端(Client)测试

调用模板方法,观察不同热饮的制作流程:

public class Client {
    public static void main(String[] args) {
        // 制作茶
        System.out.println("=== 制作茶 ===");
        HotDrinkMaker tea = new TeaMaker();
        tea.makeDrink();

        // 制作咖啡
        System.out.println("=== 制作咖啡 ===");
        HotDrinkMaker coffee = new CoffeeMaker();
        coffee.makeDrink();

        // 制作黑咖啡(不加调料)
        System.out.println("=== 制作黑咖啡 ===");
        HotDrinkMaker blackCoffee = new BlackCoffeeMaker();
        blackCoffee.makeDrink();
    }
}

输出结果

=== 制作茶 ===
步骤1:烧开水(100℃)
步骤2:用沸水浸泡茶叶
步骤3:将热饮倒入杯子
步骤4:加入柠檬片
热饮制作完成!

=== 制作咖啡 ===
步骤1:烧开水(100℃)
步骤2:用沸水冲泡咖啡粉
步骤3:将热饮倒入杯子
步骤4:加入牛奶和糖
热饮制作完成!

=== 制作黑咖啡 ===
步骤1:烧开水(100℃)
步骤2:用沸水冲泡黑咖啡粉
步骤3:将热饮倒入杯子
热饮制作完成!

三、模板方法模式的关键细节

  • 模板方法:用final修饰,确保算法结构不被子类修改,是整个模式的核心。
  • 抽象方法:由子类实现,对应算法中“可变”的步骤(如brew()addCondiments())。
  • 具体方法:由父类实现,对应算法中“不变”的通用步骤(如boilWater()),子类可直接复用。
  • 钩子方法:父类提供默认实现(如wantCondiments()),子类可重写以控制算法流程(如是否执行某个步骤),增强灵活性。

四、模板方法模式的优点

  1. 固定流程,减少重复:将算法的通用步骤抽离到父类,避免子类重复编写,符合“干燥原则(DRY)”。
  2. 灵活扩展:子类只需关注自身特有的步骤实现,无需关心整体流程,符合“开闭原则”(新增功能只需新增子类)。
  3. 控制反转:父类定义“做什么”(流程),子类定义“怎么做”(细节),实现了对算法的集中管控。

通过这个例子,我们可以清晰看到:模板方法模式通过“骨架+细节”的分离,完美解决了“流程固定但步骤实现多变”的场景问题(如框架中的生命周期方法、报表生成、流程引擎等)。它是框架设计中常用的模式,能有效提升代码的复用性和可维护性。

posted @ 2025-11-05 10:21  fishyy  阅读(3)  评论(0)    收藏  举报