深入浅出设计模式【二十四、终章———选择指南、过渡设计、实际案例、经验教训】
1. 设计模式对比与选择指南
相似模式的区别与适用场景
理解相似模式之间的微妙差别是正确选择的关键。以下是一些常见易混淆模式的对比:
| 模式对 | 核心区别 | 适用场景 |
|---|---|---|
| 策略 (Strategy) vs 状态 (State) | 意图不同:策略是主动选择算法,客户端明确知道不同策略的存在;状态是被动响应,状态转换由内部触发,对客户端透明。 | 策略:支付方式、折扣计算、排序算法。 状态:订单状态机、游戏角色行为、工作流引擎。 |
| 装饰器 (Decorator) vs 代理 (Proxy) | 目的不同:装饰器旨在增强功能(添加新职责);代理旨在控制访问(延迟加载、权限控制、日志记录)。结构相似,意图是区分的关键。 | 装饰器:Java I/O Streams、为对象动态添加功能。 代理:Spring AOP、远程代理、虚拟代理。 |
| 外观 (Facade) vs 中介者 (Mediator) | 耦合方向不同:外观是单向的简化接口,为客户端提供一个简单入口;中介者是双向的协调中心,同事对象之间通过中介者相互通信。 | 外观:简化复杂子系统调用、API网关。 中介者:GUI组件交互、多对象间复杂通信的协调。 |
| 组合 (Composite) vs 装饰器 (Decorator) | 结构不同:组合构建树形结构(部分-整体);装饰器构建链式结构(包裹增强)。 | 组合:文件系统、UI组件树、组织架构。 装饰器:动态添加功能,如I/O流处理。 |
| 观察者 (Observer) vs 发布-订阅 (Pub/Sub) | 耦合度不同:观察者是直接通知,观察者通常知道主题;发布-订阅是通过中介(消息代理),发布者和订阅者完全解耦,不知道对方存在。 | 观察者:GUI事件监听、模型-视图同步。 发布-订阅:微服务间异步通信、系统集成、事件总线。 |
| 命令 (Command) vs 策略 (Strategy) | 粒度不同:命令封装一个请求为对象(包含接收者、操作);策略封装一个算法为对象。命令可用于实现策略。 | 命令:实现撤销/重做、任务队列、事务。 策略:替换算法,如支付策略、计算策略。 |
如何根据需求选择合适模式
选择模式是一个决策过程,而非简单匹配。遵循以下决策流程可以大大提高模式选择的准确性:
核心原则:
- 首先,不要使用模式: 从一个简单、清晰的实现开始。当发现它难以维护、扩展或存在重复代码时,再考虑引入模式进行重构。
- 识别变化点: 找到系统中可能变化的部分。模式的作用就是封装变化。将变化的部分与不变的部分分离开来。
- 关注意图,而非结构: 两个模式可能结构相似,但意图不同(如策略vs状态)。问自己:“我真正需要解决的是什么问题?”
- 考虑未来变化: 如果某个方向的变化非常频繁,就选择支持那个方向扩展的模式(如策略模式支持算法扩展,访问者模式支持操作扩展)。
模式组合使用的实践
设计模式很少单独使用,组合使用可以产生更强大的力量:
-
组合模式 + 访问者模式:
- 场景: 遍历一个复杂的树形结构(如AST、UI组件树),并对每个节点执行多种操作(如渲染、序列化、验证)。
- 如何组合: 组合模式负责构建树形结构,访问者模式负责对树中的每个节点执行操作,避免了将操作代码污染到节点类中。
-
工厂模式 + 原型模式:
- 场景: 需要创建多种类型的复杂对象,且创建成本较高。
- 如何组合: 工厂类内部维护一个原型对象注册表。当客户端请求新对象时,工厂不是从头创建,而是克隆(原型模式)一个已存在的原型对象,并可能进行一些初始化配置。
-
装饰器模式 + 责任链模式:
- 场景: 构建一个可灵活扩展的过滤器链或拦截器链(如Servlet Filter、Spring Interceptor)。
- 如何组合: 每个过滤器都是一个装饰器,它们被组织成一条责任链。请求和响应对象沿着链条传递,每个过滤器都可以装饰(增强或过滤)它们。
-
观察者模式 + 中介者模式:
- 场景: 在一个事件驱动的系统中,需要集中管理复杂的事件响应逻辑。
- 如何组合: 组件之间不直接通信,而是发布事件到中介者(事件总线)。中介者负责将事件路由给相应的观察者(事件处理器),起到了解耦和协调的作用。
2. 反模式与常见误区
常见的设计误用
-
单例模式 (Singleton) 的滥用:
- 误用: 将其作为全局变量桶使用,破坏了封装性,导致代码难以测试(无法模拟替代实例)。
- 正确做法: 谨慎使用单例,仅用于表示真正的全局唯一实例(如线程池、配置管理器)。优先考虑依赖注入(DI)来管理对象的生命周期和依赖关系。
-
强迫使用模式:
- 误用: 在不需要的地方生搬硬套模式,使简单问题复杂化。
- 正确做法: 遵循“最简单且能工作的方案”原则,在需要时再重构引入模式。
-
过度工程 (Over-Engineering):
- 误用: 为一个可能永远不会变化的部分设计灵活的、基于模式的解决方案。
- 正确做法: 实践YAGNI原则(You Ain’t Gonna Need It)。除非变化确实可能发生,否则不要为它做设计。
过度设计问题
过度设计通常源于对“完美架构”的追求,其代价是:
- 复杂度飙升: 引入了大量不必要的抽象层和间接调用,使代码难以理解和调试。
- 开发效率降低: 编写“灵活”的代码比编写“直接”的代码需要更多时间。
- 维护成本增加: 其他开发者需要花费更多精力来理解你的设计。
如何避免: 从简单设计开始,通过持续重构来适应变化的需求。让设计随着对问题域的理解加深而自然演进。
模式适用的边界条件
每个模式都有其适用的前提,忽略这些前提会导致失败:
- 访问者模式: 要求对象结构稳定。如果频繁添加新的元素类,就需要修改所有访问者接口,这将是灾难性的。
- 迭代器模式: 如果集合太小且简单,直接使用
for循环可能比创建迭代器更简单高效。 - 享元模式: 仅在需要创建大量细粒度对象且这些对象有共享状态时才有效。否则,管理共享池的开销可能超过其收益。
- 中介者模式: 容易演变为上帝对象,集中了所有交互逻辑,变得臃肿且难以维护。
3. 设计模式在实际项目中的应用案例
真实项目中的模式应用
案例:电商平台订单系统
| 模式 | 应用场景 | 具体实现与收益 |
|---|---|---|
| 状态模式 | 订单生命周期管理(待支付、已支付、已发货、已完成、已取消)。 | 每个状态一个类,封装了该状态下所有允许的操作和状态转换规则。收益: 消除了庞大的 if-else 分支,新状态易于添加,状态转换逻辑清晰。 |
| 策略模式 | 折扣计算、支付方式选择、运费计算。 | 为每种计算方式定义一个策略类。收益: 轻松支持促销活动(新增折扣策略),支付渠道扩容(新增支付策略),符合开闭原则。 |
| 观察者模式 | 订单状态变更通知。 | 订单(主题)状态改变时,自动通知观察者(如:发送短信、邮件、更新用户界面、触发积分计算)。收益: 订单核心逻辑与通知逻辑解耦,通知方式易于扩展。 |
| 工厂模式 | 创建复杂的订单对象(可能包含商品、优惠券、用户信息等聚合)。 | 使用工厂方法或抽象工厂封装订单对象的组装逻辑。收益: 简化客户端代码,统一创建逻辑,易于切换不同的对象构建方式。 |
| 代理模式 | 订单服务的访问控制和日志记录。 | 为订单服务接口创建代理,在调用真实服务前后进行权限校验和日志记录。收益: 业务逻辑与非功能性需求(安全、日志)分离,符合单一职责原则。通常通过Spring AOP实现。 |
| 门面模式 | 提供统一的订单操作API。 | 创建一个门面类,封装下单、支付、查询等涉及多个子系统的复杂调用。收益: 为客户端提供一个简单易用的接口,隐藏了内部微服务的复杂性。 |
模式带来的收益与代价
| 模式 | 典型收益 | 常见代价 |
|---|---|---|
| 大多数模式 | 解耦、提高灵活性/扩展性、提高可读性/可维护性、代码复用。 | 增加复杂度: 引入更多类和接口。 性能开销: 增加间接调用(通常可忽略)。 学习曲线: 新成员需要时间理解设计。 |
| 抽象工厂/工厂方法 | 隐藏创建细节,支持产品族。 | 添加新产品类型需修改工厂接口。 |
| 适配器 | 让不兼容的接口协同工作。 | 过多使用会使系统混乱,不如统一接口。 |
| 观察者 | 实现松耦合的事件通知。 | 通知顺序不确定,可能引发循环依赖。 |
| 访问者 | 易于添加新操作。 | 难以添加新元素,破坏封装性。 |
经验教训总结
- 模式是手段,不是目标: 永远以解决实际问题为出发点,而不是为了使用模式而使用模式。
- 重构优于预先设计: 很难在项目初期就设计出完美的模式应用。更佳实践是:先让代码工作,然后在识别出代码异味(Code Smells)时,通过重构引入模式。
- 理解比记忆更重要: 深刻理解模式的意图和适用场景,比死记硬背其结构更重要。这样才能在正确的时机选择正确的模式。
- 基础设施是模式的最佳伴侣: Spring等现代框架本身就大量使用了工厂、代理、模板方法等模式。理解这些框架的工作原理,能帮助你更好地在业务代码中应用模式。
- 简洁至上: 最有效的设计往往是那个最简单、最直接的设计。模式的威力在于管理复杂性,而不是增加复杂性。
结论: 设计模式是资深工程师工具箱中强大的工具,但它们需要经验和判断力来正确使用。始终在简单性和灵活性之间做出明智的权衡,并记住:可工作的、清晰的代码,远比充满模式但难以理解的代码更有价值。
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19120410

浙公网安备 33010602011771号