Java 23 种设计模式:从踩坑到精通 | 番外:策略 vs 模板方法 —— 组合与继承的终极对决

Java 23 种设计模式:从踩坑到精通 | 番外:策略 vs 模板方法 —— 组合与继承的终极对决

摘要:策略模式和模板方法模式都能消除重复代码,但策略用“组合”替换整体算法,模板方法用“继承”固定算法骨架。它们的核心区别在于控制权的归属——策略模式由客户端选择算法,模板方法由父类控制流程。本文从电商促销和数据同步两个实战场景出发,结合代码对比、UML 分析和 JDK 源码,带你彻底理解“组合优于继承”的真正含义。

🗺️本文阅读地图(3 分钟速览)

  • 为什么策略和模板方法都能消除重复?它们到底像在哪里?
  • UML 对比:接口+组合 vs 抽象类+继承
  • 手写促销策略引擎 vs 数据同步模板,感受差异
  • DKComparator(策略) vsHttpServlet(模板方法)源码对比
  • 面试必问:“什么时候用策略?什么时候用模板方法?”

📖《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 |当前:番外 · 策略 vs 模板方法
🔗 返回系列总目录


文章目录

  • Java 23 种设计模式:从踩坑到精通 | 番外:策略 vs 模板方法 —— 组合与继承的终极对决
    • 1. 从“电商促销”和“数据同步”两个场景说起
      • 1.1 场景一:电商促销(策略模式)
      • 1.2 场景二:数据同步(模板方法模式)
    • 2. UML 对比:接口+组合 vs 抽象类+继承
      • 2.1 策略模式 UML(组合)
      • 2.2 模板方法模式 UML(继承)
    • 3. 四大核心区别
    • 4. JDK 源码对比
      • 4.1 `Comparator`(策略模式)
      • 4.2 `HttpServlet`(模板方法模式)
    • 5. 面试必问 + 追问连环炮
    • 🧭 《Java 23 种设计模式:从踩坑到精通》快速导航

1. 从“电商促销”和“数据同步”两个场景说起

1.1 场景一:电商促销(策略模式)

电商系统有多种促销算法——满减、打折、立减。不同活动、不同用户可能使用不同的促销策略。这些算法整体可替换,你需要在运行时灵活选择用哪种促销。

// 策略模式:客户端选择算法Orderorder=newOrder(500);order.setPromotionStrategy(newFullReductionStrategy());// 满减doubleprice1=order.checkout();order.setPromotionStrategy(newDiscountStrategy());// 切换打折doubleprice2=order.checkout();

💬特征:算法之间完全独立,客户端显式选择使用哪个策略。

1.2 场景二:数据同步(模板方法模式)

不同数据源的同步流程大体相同——连接、抽取、转换、加载、断开。这些步骤的流程固定,但“抽取什么数据”、“怎么转换”各不相同。

// 模板方法模式:父类固定流程,子类填空publicabstractclassDataSyncTemplate{publicfinalvoidsync(){// 模板方法:final,防止篡改connect();extractData();// 子类实现transformData();// 钩子:可选覆盖loadData();// 子类实现disconnect();}// ...}

💬特征:流程骨架固定,子类只能填空,不能改变顺序。


2. UML 对比:接口+组合 vs 抽象类+继承

2.1 策略模式 UML(组合)

2.2 模板方法模式 UML(继承)

UML 的关键差异

  • 策略模式使用接口 + 组合Context持有Strategy引用,由客户端注入;
  • 模板方法模式使用抽象类 + 继承,父类定义骨架,子类通过继承实现可变步骤;
  • 策略的Context提供setStrategy()方法,模板方法的父类不提供切换方法——因为骨架是固定的。

3. 四大核心区别

对比维度策略模式模板方法模式
核心机制接口 + 组合抽象类 + 继承
控制权客户端选择算法父类控制流程,子类只填空
替换粒度整个算法可替换算法骨架固定,部分步骤可变
复用方式复用策略类(组合)复用父类通用代码(继承)
典型应用Comparator、支付方式HttpServletJdbcTemplate

💡简单记忆:策略是“组合替换”——整个算法随便换;模板方法是“继承填空”——骨架固定,填细节。


4. JDK 源码对比

4.1Comparator(策略模式)

// Comparator 是策略接口,每种排序规则是一个具体策略List<String>list=Arrays.asList("apple","banana","cherry");list.sort(Comparator.naturalOrder());// 正序策略list.sort(Comparator.reverseOrder());// 倒序策略list.sort(Comparator.comparing(String::length));// 按长度策略

💬 客户端显式选择排序策略,策略之间完全独立。

4.2HttpServlet(模板方法模式)

// HttpServlet 是模板父类,service() 是模板方法publicclassMyServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp){// 只填空:处理 GET 请求}@OverrideprotectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp){// 只填空:处理 POST 请求}}

💬service()方法是final的,流程固定——子类不能改变“先判断方法类型再分发”的骨架。


5. 面试必问 + 追问连环炮

基础必问

  • 策略模式和模板方法模式的区别?
    👉 策略用组合替换整体算法(客户端选择),模板方法用继承固定骨架(父类控制流程)。
  • 什么时候用策略?什么时候用模板方法?
    👉 算法整体可替换、需要灵活组合 → 策略;流程固定、只有部分步骤不同 → 模板方法。

面试官追问

  • “为什么说策略模式体现了‘组合优于继承’?”
    👉 策略通过接口+组合来替换算法,可以在运行时动态切换;继承只能在编译时确定,且子类受限于父类的骨架设计。组合比继承更灵活。
  • “模板方法模式中钩子方法的作用?”
    👉 钩子方法提供默认实现,子类可选覆盖。它让父类在固定流程中留出“可开关的分支”——例如wantCondiments()控制是否加调料,子类重写它就能改变流程行为。
  • JdbcTemplate是策略还是模板方法?”
    👉 模板方法模式。JdbcTemplate固定了“获取连接→执行SQL→处理结果集→释放资源”的流程,用户只通过回调接口填写 SQL 和结果映射逻辑。
  • “策略模式和模板方法能配合使用吗?”
    👉 可以。模板方法固定流程,策略在流程中的某个步骤提供多种算法选择。例如数据同步框架中,extractData()步骤可以用策略模式支持多种数据源(MySQL、PostgreSQL、MongoDB)。

🎉恭喜:如果你能立刻说出Comparator是策略模式(组合替换),HttpServlet是模板方法模式(继承填空),你已经掌握了行为型模式中最核心的“组合 vs 继承”设计哲学。

💡延伸阅读:本文是策略模式与模板方法模式的深度对比。如果你想先了解每种模式的完整原理、UML、代码实现和面试题,可以先阅读正篇——策略模式:算法族的封装与切换,告别 if-else 和 模板方法模式:定义算法骨架,交给子类填充细节,再回来看这篇对比,效果更佳。

🧭 《Java 23 种设计模式:从踩坑到精通》快速导航

  • 开篇:系列介绍与目录
  • 当前:番外 · 策略 vs 模板方法 —— 组合与继承的终极对决(你在这里)
  • 📖 延伸阅读:策略模式正篇 | 模板方法模式正篇
  • 创建型模式汇总
  • 结构型模式汇总
  • 行为型模式汇总

🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。

📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》。技术相通,思路可鉴。