【西瓜带你学设计模式 | 第十一期 - 模板方法模式】模板方法模式 —— 流程骨架与钩子实现、优缺点与适用场景

张开发
2026/4/3 19:23:32 15 分钟阅读
【西瓜带你学设计模式 | 第十一期 - 模板方法模式】模板方法模式 —— 流程骨架与钩子实现、优缺点与适用场景
文章目录前言1. 模板方法模式是什么2. 模板方法模式解决什么问题3. 实现步骤4. 静态结构示例4.1 抽象模板类定义骨架流程4.2 子类实现可变步骤4.3 客户端只调用模板方法5. 动态结构示例“流程骨架 运行时替换点”6. 钩子方法Hook7. 和代理模式对比7.1 结构相似意图不同7.2 关键差异8. 优缺点8.1 优点8.2 缺点9. 总结前言在面向对象设计里有一种“访问前后要做事”的需求。但代理模式Proxy更侧重于“替你去访问并控制访问”。而模板方法模式Template Method Pattern更关心的是一套固定流程骨架算法怎么写才能复用哪些步骤允许子类替换它用来解决“流程很固定、但流程中的某些步骤可能变化”的问题。1. 模板方法模式是什么模板方法模式在抽象类中定义一个算法的骨架模板方法把通用步骤放在父类里而把一些可变步骤延迟到子类中实现。核心思想可以概括为父类定义“流程怎么走”固定不变的骨架子类实现“流程里的可变步骤”不同实现客户端通常只调用父类提供的模板方法流程由它驱动GoF 经典结构AbstractClass抽象类/模板类定义模板方法 通用逻辑ConcreteClass具体子类实现抽象步骤/覆盖钩子步骤2. 模板方法模式解决什么问题常见场景就是流程固定但步骤可变。例如数据处理流水线校验 - 转换 - 入库转换规则因类型不同而不同。算法流程复用例如某类“关卡通关流程”判断条件/奖励发放在不同关卡不同。游戏/业务的步骤化执行登录校验 - 扣费 - 下单扣费策略可能不同。框架级流程半成品流程例如某些框架的“执行器”通用执行流程固定可扩展点交给你填。3. 实现步骤写模板方法模式通常分为这几步抽象类里写模板方法固定流程使用final强烈建议让流程骨架不被子类篡改。把可变步骤声明为抽象方法或可覆盖方法抽象方法子类必须实现钩子方法Hook提供默认实现或让子类可选择覆盖子类只关心“自己要替换的步骤”不需要关心流程拼装顺序客户端调用模板方法由父类驱动整个流程4. 静态结构示例下面用一个“做饭流程”的例子来体现步骤顺序固定但切菜/烹饪方式可变。4.1 抽象模板类定义骨架流程publicabstractclassCookTemplate{// 模板方法流程骨架固定publicfinalvoidcook(){boilWater();// 固定步骤1prepare();// 可变步骤2交给子类cookFood();// 可变步骤3交给子类plate();// 固定步骤4}// 固定步骤父类直接实现privatevoidboilWater(){System.out.println(煮水中...);}// 可变步骤交给子类实现抽象方法protectedabstractvoidprepare();// 可变步骤交给子类实现抽象方法protectedabstractvoidcookFood();// 固定步骤privatevoidplate(){System.out.println(装盘完成);}}4.2 子类实现可变步骤publicclassNoodleCookextendsCookTemplate{Overrideprotectedvoidprepare(){System.out.println(准备面条和调料);}OverrideprotectedvoidcookFood(){System.out.println(煮面并调味);}}publicclassRiceCookextendsCookTemplate{Overrideprotectedvoidprepare(){System.out.println(准备米饭和配料);}OverrideprotectedvoidcookFood(){System.out.println(蒸饭并加入配料);}}4.3 客户端只调用模板方法publicclassClient{publicstaticvoidmain(String[]args){CookTemplatecook1newNoodleCook();cook1.cook();CookTemplatecook2newRiceCook();cook2.cook();}}输出效果顺序一致差异在子类步骤煮水中…准备面条和调料 / 准备米饭和配料煮面并调味 / 蒸饭并加入配料装盘完成5. 动态结构示例“流程骨架 运行时替换点”你可以理解为模板方法模式的“动态性”来自两点cook()是固定骨架父类不变prepare()/cookFood()在运行时由子类决定也就是说流程模板固定但“插入点”是多态的。6. 钩子方法Hook有时某个步骤不是“必须替换”而是“可选增强/可决定跳过”。例如决定是否执行某个动作publicabstractclassPaymentTemplate{publicfinalvoidpay(){verify();// 固定if(shouldDiscount()){// 钩子默认 false允许子类覆盖discount();}charge();// 固定notifyUser();// 固定}protectedvoidverify(){System.out.println(校验订单);}protectedbooleanshouldDiscount(){returnfalse;}// 钩子默认不打折protectedvoiddiscount(){System.out.println(执行打折);}protectedvoidcharge(){System.out.println(扣款);}protectedvoidnotifyUser(){System.out.println(通知用户);}}publicclassVipPaymentTemplateextendsPaymentTemplate{OverrideprotectedbooleanshouldDiscount(){returntrue;// VIP 开启打折}}7. 和代理模式对比7.1 结构相似意图不同代理模式你不直接调用真实对象而是“通过一个代表去控制访问并增强”模板方法流程由模板类驱动“步骤由子类替换”强调“算法骨架复用”7.2 关键差异谁控制顺序代理模式通常是Proxy在方法里“调用前后增强”但顺序不一定是“算法骨架固定”模板方法父类明确规定“流程顺序 模板方法骨架”子类只填空扩展点是什么代理模式扩展点是“访问前/访问后拦截逻辑”权限、日志、缓存…模板方法扩展点是“流程中的某些步骤实现”准备/执行/收尾等复用的是什么代理模式复用的是“控制访问 增强”的包装能力模板方法复用的是“一整套固定流程算法骨架”8. 优缺点8.1 优点复用流程骨架避免多个子类重复写相同的流程顺序提高扩展性子类只改可变步骤符合开闭原则降低出错概率模板方法可用final固定骨架减少流程被改坏的风险8.2 缺点类层级容易膨胀可变步骤很多时子类数量可能变多不适合流程差异很大的情况如果每个实现连顺序都差很多就不适合模板方法强耦合继承结构模板方法主要依赖继承如果你不想用继承可以考虑策略模式等9. 总结模板方法模式的精髓是把一个算法/流程的“骨架”固定在父类里把某些可变步骤交给子类实现。这样既能复用整体流程又能灵活替换其中的实现点。

更多文章