1. 为什么后端开发者必须掌握设计模式
作为一名从业十年的后端工程师,我见过太多因为缺乏设计模式思维而导致的"祖传代码"。上周刚接手的一个电商项目就是典型案例——订单模块的3000行代码全部塞在一个Service类里,每次修改需求都像在拆炸弹。
设计模式不是象牙塔里的理论,而是解决特定场景下代码组织问题的经典方案。当你在处理高并发订单时,用观察者模式解耦支付成功后的通知逻辑;当你在设计权限系统时,用责任链模式实现多级审批流程;当你在优化接口性能时,用代理模式实现缓存层...这些都不是纸上谈兵,而是我亲身踩坑后总结的生存技能。
2. 创建型模式:对象诞生的艺术
2.1 单例模式(Singleton)的现代实现
你以为单例就是简单的private构造函数?来看看这个双重检查锁的线程安全实现:
public class DatabaseConnection { private static volatile DatabaseConnection instance; private DatabaseConnection() {} public static DatabaseConnection getInstance() { if (instance == null) { synchronized (DatabaseConnection.class) { if (instance == null) { instance = new DatabaseConnection(); } } } return instance; } }注意:在Spring等现代框架中,通常用@Bean + @Scope("singleton")替代手动实现,但理解底层原理对排查内存泄漏至关重要。
2.2 建造者模式(Builder)解决复杂对象构造
当你的实体类有20个字段,其中15个是可选参数时,构造函数就会变成灾难。建造者模式给出优雅解法:
public class User { private final String name; // 必选 private final String email; // 必选 private final int age; // 可选 private final String address; // 可选 private User(Builder builder) { this.name = builder.name; this.email = builder.email; this.age = builder.age; this.address = builder.address; } public static class Builder { private final String name; private final String email; private int age = 0; private String address = ""; public Builder(String name, String email) { this.name = name; this.email = email; } public Builder age(int age) { this.age = age; return this; } public Builder address(String address) { this.address = address; return this; } public User build() { return new User(this); } } } // 使用示例 User user = new User.Builder("张三", "zhangsan@example.com") .age(30) .address("北京市海淀区") .build();3. 结构型模式:代码骨架的搭建技巧
3.1 适配器模式(Adapter)实战:兼容老旧系统
去年我们整合一个2010年的支付系统时,它的接口长这样:
public class LegacyPayment { public void makePayment(String cardType, String cardNumber, float amount, int expiryMonth, int expiryYear) { // 老式支付逻辑 } }而我们的新系统需要统一调用接口:
public interface ModernPayment { void pay(CreditCard card, BigDecimal amount); }适配器解决方案:
public class PaymentAdapter implements ModernPayment { private LegacyPayment legacyPayment; public PaymentAdapter(LegacyPayment legacyPayment) { this.legacyPayment = legacyPayment; } @Override public void pay(CreditCard card, BigDecimal amount) { legacyPayment.makePayment( card.getType().name(), card.getNumber(), amount.floatValue(), card.getExpiry().getMonthValue(), card.getExpiry().getYear() ); } }3.2 装饰器模式(Decorator)实现动态扩展
假设有个基础的短信通知服务:
public interface Notifier { void send(String message); } public class BasicNotifier implements Notifier { @Override public void send(String message) { // 基础短信发送逻辑 } }现在需要在不修改原有代码的情况下,增加加密和日志功能:
public abstract class NotifierDecorator implements Notifier { protected Notifier wrapped; public NotifierDecorator(Notifier notifier) { this.wrapped = notifier; } } public class EncryptedNotifier extends NotifierDecorator { public EncryptedNotifier(Notifier notifier) { super(notifier); } @Override public void send(String message) { String encrypted = AES.encrypt(message); wrapped.send(encrypted); } } public class LoggingNotifier extends NotifierDecorator { public LoggingNotifier(Notifier notifier) { super(notifier); } @Override public void send(String message) { System.out.println("Sending: " + message); wrapped.send(message); } } // 组合使用 Notifier notifier = new LoggingNotifier( new EncryptedNotifier( new BasicNotifier())); notifier.send("重要通知:系统将于今晚升级");4. 行为型模式:对象间的协作之道
4.1 观察者模式(Observer)在订单系统的应用
电商系统中,订单状态变化时需要触发多种操作:
public interface OrderObserver { void update(Order order); } public class PaymentService implements OrderObserver { @Override public void update(Order order) { if (order.getStatus() == OrderStatus.PAID) { // 处理支付成功逻辑 } } } public class InventoryService implements OrderObserver { @Override public void update(Order order) { if (order.getStatus() == OrderStatus.SHIPPED) { // 扣减库存 } } } public class Order { private List<OrderObserver> observers = new ArrayList<>(); private OrderStatus status; public void addObserver(OrderObserver observer) { observers.add(observer); } public void setStatus(OrderStatus status) { this.status = status; notifyObservers(); } private void notifyObservers() { for (OrderObserver observer : observers) { observer.update(this); } } }实际项目中建议使用Spring Event或Guava EventBus等框架,但理解观察者模式原理才能用好这些工具。
4.2 策略模式(Strategy)实现多算法切换
支付系统需要支持多种支付方式:
public interface PaymentStrategy { void pay(BigDecimal amount); } public class AlipayStrategy implements PaymentStrategy { @Override public void pay(BigDecimal amount) { // 支付宝支付逻辑 } } public class WechatPayStrategy implements PaymentStrategy { @Override public void pay(BigDecimal amount) { // 微信支付逻辑 } } public class PaymentContext { private PaymentStrategy strategy; public void setStrategy(PaymentStrategy strategy) { this.strategy = strategy; } public void executePayment(BigDecimal amount) { strategy.pay(amount); } } // 使用示例 PaymentContext context = new PaymentContext(); context.setStrategy(new AlipayStrategy()); context.executePayment(new BigDecimal("100.00"));5. 并发编程中的设计模式
5.1 生产者-消费者模式实战
处理日志异步写入的经典场景:
public class LogManager { private BlockingQueue<String> queue = new LinkedBlockingQueue<>(1000); private ExecutorService executor = Executors.newFixedThreadPool(2); public void start() { executor.submit(this::producer); executor.submit(this::consumer); } private void producer() { while (true) { String log = generateLog(); // 模拟日志生成 try { queue.put(log); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } private void consumer() { while (!Thread.currentThread().isInterrupted()) { try { String log = queue.take(); writeToFile(log); // 实际写入文件 } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } }5.2 线程池中的工作窃取模式
Java的ForkJoinPool就是典型实现:
public class DocumentSearchTask extends RecursiveTask<Long> { private final Document document; private final String searchWord; public DocumentSearchTask(Document document, String searchWord) { this.document = document; this.searchWord = searchWord; } @Override protected Long compute() { if (document.getPages().size() <= 10) { return countWordInDocument(); } List<DocumentSearchTask> subtasks = new ArrayList<>(); for (Page page : document.getPages()) { subtasks.add(new DocumentSearchTask(page, searchWord)); } return ForkJoinTask.invokeAll(subtasks) .stream() .mapToLong(ForkJoinTask::join) .sum(); } private long countWordInDocument() { // 实际统计逻辑 } } // 使用示例 ForkJoinPool pool = new ForkJoinPool(); Document document = loadLargeDocument(); long count = pool.invoke(new DocumentSearchTask(document, "设计模式"));6. 设计模式的组合与反模式
6.1 模式组合案例:Spring中的模板方法+策略模式
Spring的JdbcTemplate就是经典组合:
public class UserRepository { private JdbcTemplate jdbcTemplate; public User findById(Long id) { return jdbcTemplate.query( "SELECT * FROM users WHERE id = ?", new Object[]{id}, rs -> { if (!rs.next()) return null; User user = new User(); user.setId(rs.getLong("id")); user.setName(rs.getString("name")); return user; }); } }这里:
- JdbcTemplate使用模板方法模式定义了执行SQL的固定流程
- RowMapper接口使用策略模式允许自定义结果映射
6.2 常见反模式警示
- 单例滥用:把本应是无状态的工具类强行做成单例,导致测试困难
- 过度设计:在简单CRUD项目中使用所有23种设计模式
- 模式混用:在同一个类中同时使用策略模式和状态模式,导致逻辑混乱
- 忽略语言特性:在Java 8+中仍用传统方式实现观察者模式,而非使用函数式接口
7. 设计模式在现代框架中的应用
7.1 Spring中的设计模式
| 设计模式 | Spring实现示例 | 应用场景 |
|---|---|---|
| 工厂模式 | BeanFactory | 对象创建 |
| 代理模式 | @Transactional | AOP实现 |
| 模板方法 | JdbcTemplate | 数据库操作 |
| 观察者模式 | ApplicationEvent | 事件驱动 |
| 适配器模式 | HandlerAdapter | 处理不同类型的Controller |
7.2 MyBatis中的设计模式
- 建造者模式:SqlSessionFactoryBuilder
- 工厂模式:SqlSessionFactory
- 代理模式:Mapper接口的实现
- 装饰器模式:缓存模块的实现
8. 如何在实际项目中合理应用
8.1 识别模式应用场景的思维框架
- 当你在重复编写相似代码时 → 考虑模板方法模式
- 当对象创建逻辑变得复杂时 → 考虑工厂或建造者模式
- 当模块间存在紧耦合时 → 考虑观察者或中介者模式
- 当需要动态切换算法时 → 考虑策略模式
- 当接口不兼容需要适配时 → 考虑适配器模式
8.2 代码重构实战:从过程式到模式化
重构前:
public class OrderService { public void processOrder(Order order) { // 验证订单 if (order.getItems().isEmpty()) { throw new IllegalArgumentException("订单不能为空"); } // 计算总价 BigDecimal total = BigDecimal.ZERO; for (Item item : order.getItems()) { total = total.add(item.getPrice()); } order.setTotal(total); // 支付处理 if (order.getPaymentMethod().equals("alipay")) { // 支付宝支付逻辑 } else if (order.getPaymentMethod().equals("wechat")) { // 微信支付逻辑 } // 库存扣减 for (Item item : order.getItems()) { inventoryService.reduce(item.getId(), item.getQuantity()); } // 发送通知 emailService.send(order.getCustomerEmail(), "订单创建成功"); smsService.send(order.getCustomerPhone(), "订单创建成功"); } }重构后:
public class OrderService { private List<OrderValidator> validators; private PaymentStrategyFactory paymentFactory; private NotificationService notificationService; public void processOrder(Order order) { // 责任链模式验证 validators.forEach(v -> v.validate(order)); // 策略模式支付 PaymentStrategy strategy = paymentFactory.getStrategy(order.getPaymentMethod()); strategy.pay(order.getTotal()); // 观察者模式通知 notificationService.notify(order); } }9. 性能与可维护性的平衡
9.1 设计模式带来的性能开销
- 代理模式:方法调用多一层间接性
- 装饰器模式:多层嵌套影响调用栈深度
- 观察者模式:事件通知的异步处理开销
- 工厂模式:对象创建的多余抽象层
9.2 优化建议
- 对性能关键路径避免深度模式嵌套
- 使用对象池减少创建型模式的开销
- 考虑编译时处理(如注解处理器)替代运行时动态性
- 在框架层面统一实现模式而非业务代码中
10. 测试设计模式实现的技巧
10.1 单例模式的单元测试
关键点:重置静态实例
public class DatabaseConnectionTest { private DatabaseConnection instance1; private DatabaseConnection instance2; @BeforeEach void setUp() throws Exception { // 通过反射重置单例 Field instance = DatabaseConnection.class.getDeclaredField("instance"); instance.setAccessible(true); instance.set(null, null); } @Test void shouldReturnSameInstance() { instance1 = DatabaseConnection.getInstance(); instance2 = DatabaseConnection.getInstance(); assertSame(instance1, instance2); } }10.2 模拟观察者模式的通知
@Test void shouldNotifyAllObservers() { Order order = new Order(); OrderObserver mockObserver = mock(OrderObserver.class); order.addObserver(mockObserver); order.setStatus(OrderStatus.PAID); verify(mockObserver).update(order); }11. 从设计模式到领域驱动设计
11.1 策略模式与领域服务
将业务规则抽象为策略:
public interface DiscountStrategy { BigDecimal apply(BigDecimal amount); } public class VipDiscountStrategy implements DiscountStrategy { @Override public BigDecimal apply(BigDecimal amount) { return amount.multiply(new BigDecimal("0.9")); } } public class Order { private DiscountStrategy discountStrategy; public void setDiscountStrategy(DiscountStrategy strategy) { this.discountStrategy = strategy; } public BigDecimal calculateTotal() { BigDecimal rawTotal = calculateRawTotal(); return discountStrategy.apply(rawTotal); } }11.2 工厂模式与聚合根创建
public class OrderFactory { public Order createOrder(Customer customer, List<OrderItem> items) { Order order = new Order(); order.setCustomer(customer); order.setItems(items); if (customer.isVip()) { order.setDiscountStrategy(new VipDiscountStrategy()); } else { order.setDiscountStrategy(new DefaultDiscountStrategy()); } return order; } }12. 设计模式的演进与替代方案
12.1 Java 8+的函数式替代
观察者模式的Lambda实现:
public class Order { private List<Consumer<Order>> observers = new ArrayList<>(); public void addObserver(Consumer<Order> observer) { observers.add(observer); } private void notifyObservers() { observers.forEach(observer -> observer.accept(this)); } } // 使用 order.addObserver(o -> { if (o.getStatus() == OrderStatus.PAID) { paymentService.process(o); } });12.2 注解处理器替代模板方法
传统模板方法:
public abstract class ReportGenerator { public final void generate() { loadData(); processData(); export(); } protected abstract void loadData(); protected abstract void processData(); protected abstract void export(); }现代注解方案:
@ReportProcessor public class SalesReport { @DataLoader public void loadSalesData() { /* ... */ } @DataProcessor public void calculateTotals() { /* ... */ } @Exporter public void exportToExcel() { /* ... */ } }13. 个人实践心得与进阶建议
- 不要为了模式而模式:先写出可工作的代码,再考虑用模式重构
- 理解比记忆重要:掌握每个模式解决的问题场景,而非UML图
- 阅读框架源码:Spring、MyBatis等优秀框架是模式的最佳教材
- 从CRUD到领域建模:在简单业务中尝试应用策略、状态等模式
- 保持适度抽象:过度设计比设计不足更危险
我在微服务拆分过程中最深刻的体会是:适配器模式和门面模式是系统演进的生命线。当我们需要将单体拆分为微服务时,先用门面模式保持对外接口不变,内部逐步迁移;当对接第三方服务时,适配器模式让核心业务逻辑不受外部变化影响。这种渐进式改造策略避免了"重写所有代码"的陷阱。