策略与工厂的演进:打造工业级Spring路由框架
文章目录
- **引言:从“学术模型”到“工程产品”**
- **一、经典工厂模式的“原罪”**
- **原罪一:严重违反“开闭原则”——一场“永无止境的手术”**
- **原罪二:彻底破坏“依赖注入”——一座“脱离现代文明的孤岛”**
- **二、设计演进:注册表驱动的“智能工厂”**
- **2.1 设计的组成部分**
- **2.2 新设计如何“救赎”两大原罪**
引言:从“学术模型”到“工程产品”
设计模式的学习,最终要回归到解决复杂的业务问题上。一个卓越的设计,往往不是对某个模式的生搬硬套,而是将多种模式的思想融会贯通,并与所使用的技术框架(如Spring)深度结合。
本文将以一个复杂的“审批回调中心”为实战案例,解构一个融合了策略模式和工厂模式思想的动态路由框架。我们将首先剖析“经典工厂模式”在现代Spring项目中的两大“原罪”,然后展示一种“注册表驱动”的智能工厂是如何完成救赎,最终提炼出一套能在面试中充分展现你设计思想的“黄金回答框架”。
一、经典工厂模式的“原罪”
我们先来看看一个教学中常见,但在现代Spring工程中应被坚决避免的“经典”工厂模式是什么样的。
// 这是一个【反模式】的示例,用以对比说明
public class ClassicApprovalHandlerFactory {public static ApprovalHandler getHandler(String routeKey) {if ("0:0:0:0".equals(routeKey)) {// 问题1:手动new对象,无法利用Spring的依赖注入// 问题2:每次新增业务,都要回来修改这个if-else链,违反开闭原则return new CardSalesPayHandler(); } else if ("0:0:0:1".equals(routeKey)) {return new CardSalesEffectHandler();}// ... more and more else if ...return null;}
}
在Spring项目中,这种实现方式存在两大“原罪”。
原罪一:严重违反“开闭原则”——一场“永无止境的手术”
-
问题所在:
if-else
或switch-case
的硬编码逻辑。 -
后果:
- 高昂的维护成本与风险:每当需要支持一种新的审批类型,你必须回到这个工厂类,增加一个新的
else if
分支。修改一个如此核心的类,是一场高风险的“外科手术”,需要进行完整的回归测试。 - 频繁的团队协作冲突:在大型项目中,多位开发者很可能在同一迭代中都需要增加新的策略,这将不可避免地导致同一个文件的代码合并冲突(Git Merge Conflict)。
- 职责无限膨胀:随着业务发展,
if-else
链条会无限膨胀,最终形成一个知道了所有实现细节的“上帝类(God Class)”,严重违反了单一职责原则。
- 高昂的维护成本与风险:每当需要支持一种新的审批类型,你必须回到这个工厂类,增加一个新的
-
现实隐喻:这个经典工厂,就像一个逻辑被焊死在电路板上的机器人。想让它学会一个新动作,就必须冒着烧坏其他电路的风险,去修改它的核心主板。
原罪二:彻底破坏“依赖注入”——一座“脱离现代文明的孤岛”
这是在Spring环境下,这种做法最致命、最不可饶恕的问题。
-
问题所在:
new CardSalesPayHandler()
这个操作。 -
后果:
- 依赖全部为
null
:通过new
关键字手动创建出来的对象,是一个**“野生”对象**,它完全脱离了Spring IoC容器的管理。这意味着,其内部所有@Autowired
的依赖(如OrderService
)都将是null
,调用时会立即抛出NullPointerException
。 - AOP切面完全失效:Spring的声明式事务(
@Transactional
)、异步执行(@Async
)等强大功能,都是通过AOP为Bean创建代理对象来实现的。而new
得到的,是那个赤裸裸的、未经任何增强的原始对象,所有AOP注解都将完全失效。 - 无法享受容器生命周期:所有
@PostConstruct
初始化方法、@Value
配置注入等,都不会被执行。
- 依赖全部为
-
现实隐喻:通过
new
创建的对象,就像一个被扔到荒岛上的鲁滨逊。他没有电力供应(@Autowired
),没有安保系统(@Transactional
),也没有任何公共服务(Bean生命周期回调)。
二、设计演进:注册表驱动的“智能工厂”
现在,我们来看这个“工业级”的设计是如何解决上述所有问题的。其核心是策略模式和一种更高级的工厂模式——“注册表驱动的工厂(Registry-driven Factory)”的完美结合。
2.1 设计的组成部分
这个“智能工厂”由三个核心部件构成:
-
“零件仓库” (
handlerMap
)- 代码:
private Map<String, ApprovalHandler> handlerMap;
- 原理:通过
@Autowired
,Spring这个“后勤总管”,已经提前将所有实现了ApprovalHandler
接口的策略类(Handler
)都实例化并管理起来。然后,它会将这些Bean以其各自的Bean Name为Key,全部注入到这个Map中。 - 作用:这一步,用依赖注入代替了传统工厂里的
new ...()
操作。创建和管理对象的职责,已经从我们的代码,完全转移给了Spring IoC容器。
- 代码:
-
“生产蓝图” (
HANDLER_ROUTING_TABLE
)- 代码:
private static final Map<String, String> HANDLER_ROUTING_TABLE;
- 原理:这是一个静态
Map
,是整个工厂的“生产指令集”或“装配蓝图”。它定义了“选择逻辑”:什么样的输入(由业务线、单据类型等四个维度共同决定的复合Key),对应于哪一个“零件”(Handler的Bean Name)。 - 核心优势:它将**“选择逻辑”从硬编码(
if-else
)中解耦出来,变成了可配置的“元数据”**。
- 代码:
-
“总装车间” (核心业务逻辑方法)
- 原理:这个方法是工厂的“总装车间”,它的逻辑非常纯粹,不关心任何具体业务,只负责一套标准的“按图索骥”流水线:
- 根据输入,组装出规格型号(
routeKey
)。 - 拿着“规格型号”,去“生产蓝图”里,查阅应该使用哪个“零件名称”(
handlerName
)。 - 拿着“零件名称”,去“零件仓库”里,精确地取出那个已经预制好的、功能完备的“精英市民”(完全装配好的Bean实例)。
- 启动这个“零件”,让它工作(
handler.handle(...)
)。
- 根据输入,组装出规格型号(
- 原理:这个方法是工厂的“总装车间”,它的逻辑非常纯粹,不关心任何具体业务,只负责一套标准的“按图索骥”流水线:
2.2 新设计如何“救赎”两大原罪
-
“救赎”开闭原则:当需要新增一种审批类型时,我们不再需要修改“总装车间”的核心代码。我们只需要:
- 新增一个
Handler
策略类。 - 在“生产蓝图”(
HANDLER_ROUTING_TABLE
)中新增一行Key-Value的映射关系。
这完美地践行了对修改关闭,对扩展开放的原则。
- 新增一个
-
“救赎”依赖注入:因为我们从“零件仓库”中获取的所有
Handler
实例,都是由Spring容器创建和管理的Bean,所以它们是“全副武装”的。所有内部依赖都已注入,所有AOP切面都已生效,可以完全享受Spring生态带来的所有便利和保障。