代码界的米其林大厨:模板方法模式的烹饪艺术

99 阅读4分钟

代码界的米其林大厨:模板方法模式的烹饪艺术


一、当代码开始「照菜谱下厨」

想象这样的后厨场景:
米其林主厨写下一份秘制菜谱,规定「煎牛排必须大火锁汁→低温慢烤→静置收汁」,但允许徒弟自由发挥酱料;
奶茶店规定「加冰→加茶→加料→封口」的标准流程,但各分店可自选茶底和小料...

这就是模板方法模式的精髓——像总厨的菜谱一样,固定烹饪框架,保留个性空间,让代码既能标准化生产,又不失灵活风味!


二、总厨的秘制菜谱(UML图)

          ┌─────────────┐
          │AbstractClass│
          ├─────────────┤
          │ +template() │
          │ +step1()    │
          │ +step2()    │
          │ +step3()    │
          └──────△──────┘
                 │
   ┌─────────────┴─────────────┐
   │                           │
┌─────────────┐        ┌─────────────┐
│ Concrete    │        │   Hook      │
│ ClassA      │        ├─────────────┤
└─────────────┘        │ +hook()     │
                       └─────────────┘
  • 总厨(AbstractClass):定义做菜流程模板
  • 帮厨(ConcreteClass):实现具体步骤(比如切菜手法)
  • 口味钩子(Hook):可选调整项(比如加不加香菜)

三、代码后厨的烹饪实况(场景实战)

1. 总厨的标准化菜谱(抽象类)
abstract class BeverageMaker {
    // 模板方法(禁止子类修改final配方!)
    public final void makeBeverage() {
        boilWater();
        brew();
        pourInCup();
        if (needCondiments()) {
            addCondiments();
        }
        hook();
    }

    // 固定步骤:烧水
    private void boilWater() {
        System.out.println("🔥 烧开100℃山泉水");
    }

    // 固定步骤:倒入杯
    private void pourInCup() {
        System.out.println("🥤 倒入星巴克联名杯");
    }

    // 抽象方法:冲泡(由子类实现)
    protected abstract void brew();

    // 抽象方法:加料(由子类实现)
    protected abstract void addCondiments();

    // 钩子方法:默认加料(可选覆盖)
    protected boolean needCondiments() {
        return true;
    }

    // 钩子方法:空实现(可选扩展)
    protected void hook() {}
}
2. 徒弟的个性实现(具体类)
// 徒弟A:手冲咖啡
class CoffeeMaker extends BeverageMaker {
    protected void brew() {
        System.out.println("☕ 注入现磨云南小粒咖啡");
    }

    protected void addCondiments() {
        System.out.println("➕ 加奶加糖,拉个天鹅");
    }

    // 覆盖钩子:客人可选是否加糖
    protected boolean needCondiments() {
        Scanner sc = new Scanner(System.in);
        System.out.print("要加糖吗?(y/n):");
        return sc.nextLine().equalsIgnoreCase("y");
    }
}

// 徒弟B:养生茶
class TeaMaker extends BeverageMaker {
    protected void brew() {
        System.out.println("🍵 放入宁夏枸杞+新疆红枣");
    }

    protected void addCondiments() {
        System.out.println("🍯 加土蜂蜜和生姜片");
    }

    // 扩展钩子:泡完后保温
    protected void hook() {
        System.out.println("⏲️ 转入55℃恒温杯");
    }
}
3. 米其林品鉴时刻
public class MichelinTest {
    public static void main(String[] args) {
        System.out.println("=== 咖啡师傅登场 ===");
        BeverageMaker chef = new CoffeeMaker();
        chef.makeBeverage(); // 按模板自动执行

        System.out.println("\n=== 茶师傅登场 ===");
        chef = new TeaMaker();
        chef.makeBeverage();
    }
}

四、模板方法 vs 流水线工人:总厨与代工厂的区别

维度模板方法模式工厂模式
控制权父类掌控流程子类决定对象创建
重点步骤的标准化+定制化对象的统一生产
灵活性流程固定,步骤可变完全自由创建对象
典型场景算法框架固定对象创建场景
现实类比肯德基炸鸡标准流程汽车零件组装厂

五、代码界的米其林餐厅

  1. JdbcTemplate

    // Spring的经典实现
    jdbcTemplate.execute((Connection conn) -> {
        // 固定流程:拿连接→执行→释放
        return doBusiness(conn); // 业务代码插入点
    });
    
  2. Servlet生命周期
    init() → service() → destroy() 的固定流程,doGet/doPost由子类实现

  3. 游戏关卡设计

    abstract class GameLevel {
        final void play() {
            loadScene();
            playBGM();
            startLevel(); // 抽象方法
            calculateScore();
        }
    }
    
  4. CI/CD流水线
    编译→测试→打包→部署的标准流程,不同项目实现具体测试步骤

  5. 银行交易系统
    验证→记录日志→执行交易→发送通知的标准流程


六、防炸厨房指南(最佳实践)

  1. 模板方法加final

    public final void template() { ... } // 禁止子类篡改流程
    
  2. 钩子方法要谨慎

    // 钩子方法命名要清晰
    protected boolean customerWantsSugar() { ... }
    
  3. 控制抽象粒度

    不要过度拆分步骤:
    ✅ 好的:brew() 冲泡
    ❌ 坏的:boilWater() → wait3min() → addTeaLeaves()
    
  4. 好莱坞原则

    "Don't call us, we'll call you."
    —— 子类不要调用父类,等父类模板来调用

  5. 与策略模式结合

    // 在模板的某个步骤使用策略模式
    void brew() {
        brewingStrategy.execute(); // 策略接口
    }
    

七、米其林颁奖典礼

模板方法模式让代码成为标准与个性并存的料理大师:

  • :用于流程固定但步骤多变的场景
  • :用钩子方法提供灵活扩展点
  • 不要:让子类破坏算法结构
  • 不要:创建过于复杂的继承链

当你在Spring中优雅使用JdbcTemplate时,请想起模板方法模式——那个在后台默默守护标准化流程的隐形总厨!