1. 设计模式究竟是什么?
我第一次接触设计模式是在一个深夜加班重构代码的时候。当时面对一个2000行的上帝类,我意识到自己需要更系统化的解决方案。设计模式不是银弹,但确实能帮我们避免重复造轮子。
设计模式本质上是针对软件设计中常见问题的可复用解决方案。就像建筑领域的标准结构方案一样,它提供了经过验证的代码组织方式。但要注意的是,模式不是代码,而是代码的组织思路。
在Java生态中,设计模式尤为常见。从早期的Swing到现代的Spring框架,模式思想无处不在。比如Spring的BeanFactory就是工厂模式的典型实现,而AOP则大量运用了代理模式。
重要提示:设计模式不是万能的,过度使用模式会导致代码过度设计。我见过最夸张的是一个简单业务逻辑套了6层装饰器模式,维护起来简直是噩梦。
2. 设计模式的三大类型与典型代表
2.1 创建型模式:对象的诞生艺术
创建型模式处理对象实例化过程。在我参与的电商项目中,商品创建就是个典型场景:
- 工厂方法模式:当需要根据不同品类(图书、服装、数码)创建不同商品对象时
- 建造者模式:适用于构造复杂对象如"促销套餐"(主商品+赠品+优惠券组合)
- 单例模式:全局配置管理器的经典实现方式
java复制// 电商系统中的工厂方法示例
public interface Product {
void display();
}
public class Book implements Product {
@Override
public void display() {
System.out.println("图书商品");
}
}
public class ProductFactory {
public Product createProduct(String type) {
switch(type) {
case "book": return new Book();
// 其他产品类型...
default: throw new IllegalArgumentException();
}
}
}
2.2 结构型模式:对象的组合之道
结构型模式关注类和对象的组合方式。在开发API网关时,我深刻体会到了:
- 适配器模式:整合不同第三方支付接口的统一适配
- 装饰器模式:为API调用添加日志、监控等横切关注点
- 代理模式:实现接口的缓存和权限控制层
java复制// API网关中的装饰器模式示例
public interface ApiClient {
String callApi(String endpoint);
}
public class LoggingDecorator implements ApiClient {
private final ApiClient wrapped;
public LoggingDecorator(ApiClient toDecorate) {
this.wrapped = toDecorate;
}
@Override
public String callApi(String endpoint) {
System.out.println("调用API: " + endpoint);
return wrapped.callApi(endpoint);
}
}
2.3 行为型模式:对象的交互哲学
行为型模式定义对象间的通信方式。在实现订单状态机时:
- 策略模式:不同支付方式(支付宝、微信)的处理策略
- 观察者模式:订单状态变更通知库存、物流等系统
- 状态模式:订单状态流转的状态机实现
3. 设计模式的七大核心原则
3.1 单一职责原则(SRP)
一个类应该只有一个引起变化的原因。我曾重构过一个"万能工具类",它同时处理:
- 字符串操作
- 日期计算
- 文件IO
- 网络请求
拆分成多个专门类后,测试和维护难度直线下降。
3.2 开闭原则(OCP)
对扩展开放,对修改关闭。在开发报表系统时,我们通过抽象出ReportGenerator接口,后续新增PDF、Excel等导出方式时,完全不需要修改已有代码。
3.3 里氏替换原则(LSP)
子类必须能够替换父类。违反这个原则的典型表现是:
java复制class Rectangle {
void setWidth(int w) {...}
void setHeight(int h) {...}
}
class Square extends Rectangle {
// 重写方法破坏父类行为
void setWidth(int w) {
super.setWidth(w);
super.setHeight(w); // 违反LSP
}
}
3.4 接口隔离原则(ISP)
客户端不应被迫依赖它不用的方法。比如:
java复制// 不良设计
interface Worker {
void work();
void eat();
}
// 应拆分为
interface Workable {
void work();
}
interface Eatable {
void eat();
}
3.5 依赖倒置原则(DIP)
高层模块不应依赖低层模块,二者都应依赖抽象。在Spring框架中,这种原则体现得淋漓尽致:
java复制// 依赖具体实现(不符合DIP)
class PaymentService {
private AlipayProcessor processor = new AlipayProcessor();
}
// 依赖抽象(符合DIP)
class PaymentService {
private PaymentProcessor processor;
public PaymentService(PaymentProcessor processor) {
this.processor = processor;
}
}
3.6 组合优于继承原则
继承带来强耦合,而组合更灵活。比如Java的IO流设计:
java复制// 通过组合不同的流实现功能扩展
InputStream in = new BufferedInputStream(
new FileInputStream("data.txt"));
3.7 迪米特法则(LoD)
最少知识原则,对象应尽可能少了解其他对象。在微服务架构中,服务间通过API网关通信就是这一原则的体现。
4. 设计模式实践中的常见误区
4.1 模式滥用:为模式而模式
我曾见过为了使用观察者模式而强行拆分的案例:一个简单的配置变更,本可以直接调用更新方法,却硬是做成了事件发布/订阅体系,徒增复杂度。
4.2 过早优化:在简单阶段引入复杂模式
创业公司初期,业务方向可能频繁调整。此时若过早引入复杂模式,反而会成为负担。我的经验法则是:当重复修改同一处代码三次以上,再考虑引入适当模式。
4.3 生搬硬套:不考虑语言特性
在JavaScript这种原型语言中强行实现经典OOP模式往往事倍功半。比如用闭包实现私有变量,比强行套用Java式的getter/setter更符合语言特性。
4.4 忽视演进:模式僵化
随着业务发展,早期合理的模式可能变得不再适用。比如单体架构下合理的工厂模式,在微服务架构中可能就需要用服务发现机制来替代。
5. 如何在实际项目中应用设计模式
5.1 识别模式适用场景
不是先选模式再找问题,而是先发现问题再寻找合适模式。常见信号包括:
- 大量重复的条件判断
- 频繁修改同一类代码
- 对象创建逻辑复杂混乱
- 多个类在做相似的事情
5.2 渐进式重构
不要试图一次性重构整个系统。我的工作流程通常是:
- 编写测试覆盖现有功能
- 小步重构,每次只应用一个模式
- 运行测试验证
- 提交代码并记录变更
5.3 模式组合使用
真实项目中常常需要组合多个模式。比如电商系统中的订单处理:
- 用工厂方法创建不同订单类型
- 用策略模式处理支付方式
- 用观察者模式通知相关系统
- 用状态模式管理订单状态流转
5.4 文档与团队共识
引入新模式时,一定要:
- 在代码注释中明确说明使用的模式
- 更新架构文档中的模式决策记录
- 与团队分享模式的使用场景和原因
6. 设计模式与新兴技术趋势
6.1 设计模式在微服务架构中的演变
传统模式在分布式环境中有新的表现:
- 单例模式 → 分布式锁/Leader选举
- 观察者模式 → 事件溯源/CQRS
- 代理模式 → API网关/Service Mesh
6.2 响应式编程中的模式应用
在Reactive系统中:
- 迭代器模式 → Reactive Streams
- 命令模式 → 函数式编程
- 责任链模式 → Reactor操作符链
6.3 AI时代的模式思考
当AI开始生成代码时,设计模式的价值在于:
- 提供标准术语便于AI理解意图
- 作为约束条件确保生成代码质量
- 帮助人类理解AI生成的代码结构
我在实际项目中最大的体会是:设计模式不是用来炫技的,而是用来解决实际问题的。当你在代码中看到"坏味道"时,合适的模式往往能带来优雅的解决方案。但记住,最简单的解决方案通常就是最好的解决方案。
