1. 设计模式为何值得速通?
设计模式就像武侠小说中的招式套路,是前人总结出来的最佳实践。我第一次接触设计模式是在2015年参与一个电商系统重构时,当时面对错综复杂的订单处理逻辑,我的导师指着代码说:"这里该用策略模式了"。那次经历让我明白,设计模式不是纸上谈兵的理论,而是解决实际问题的利器。
为什么设计模式能成为面试必问、项目必备的技能?因为它们解决了软件开发中的三类核心问题:
对象创建难题:如何优雅地创建对象?单例确保全局唯一,工厂隐藏创建细节,建造者分步构造复杂对象。
行为组织难题:如何灵活定义算法族?策略模式让算法可互换,模板方法固定流程骨架。
结构优化难题:如何扩展功能而不改原有代码?装饰者动态添加职责,适配器兼容不同接口。
提示:设计模式不是银弹,过度使用会导致代码过度设计。我的经验法则是:当你在第三次写相似结构的代码时,才考虑引入设计模式。
2. 单例模式的正确打开方式
2.1 饿汉式 vs 懒汉式
单例模式最经典的实现争议。我曾在一个高并发系统中踩过坑:用懒汉式导致订单号重复生成。来看两种实现对比:
// 饿汉式(项目启动即加载) public class EagerSingleton { private static final EagerSingleton instance = new EagerSingleton(); private EagerSingleton() {} public static EagerSingleton getInstance() { return instance; } } // 懒汉式(用时加载) public class LazySingleton { private static volatile LazySingleton instance; private LazySingleton() {} public static LazySingleton getInstance() { if (instance == null) { synchronized (LazySingleton.class) { if (instance == null) { instance = new LazySingleton(); } } } return instance; } }选择依据:
- 饿汉式适合:初始化耗时短、必定会用的场景(如配置加载)
- 懒汉式适合:初始化耗时长、可能不用的场景(如数据库连接池)
2.2 单例的现代实现
在Java中,更推荐使用枚举实现单例。这是我参与过的一个支付系统中采用的方案:
public enum PaymentGateway { INSTANCE; public void process(PaymentRequest request) { // 支付处理逻辑 } }这种写法天然防反射攻击,且序列化安全。在Spring框架中,默认的Bean作用域就是单例,但要注意区分:
- Spring单例:每个容器内唯一
- 设计模式单例:JVM内唯一
3. 工厂模式的三种段位
3.1 简单工厂(静态工厂)
适合对象创建逻辑简单的场景。比如我在物流系统中实现的运输工具工厂:
public class TransportFactory { public static Transport createTransport(String type) { switch (type) { case "truck": return new Truck(); case "ship": return new Ship(); default: throw new IllegalArgumentException(); } } }缺点:新增类型需要修改工厂类,违反开闭原则。
3.2 工厂方法模式
在开发跨平台UI框架时,我们这样处理按钮创建:
public interface ButtonFactory { Button createButton(); } public class WindowsButtonFactory implements ButtonFactory { public Button createButton() { return new WindowsButton(); } } public class MacButtonFactory implements ButtonFactory { public Button createButton() { return new MacButton(); } }每个产品对应一个工厂,符合开闭原则,但会导致类爆炸。
3.3 抽象工厂模式
这是我为电商系统设计的跨数据库访问方案:
public interface DatabaseFactory { Connection createConnection(); QueryBuilder createQueryBuilder(); } public class MySQLFactory implements DatabaseFactory { public Connection createConnection() { return new MySQLConnection(); } public QueryBuilder createQueryBuilder() { return new MySQLQueryBuilder(); } }适合创建产品族的情况,比如需要保证MySQL的Connection和QueryBuilder配套使用。
4. 建造者模式的精妙之处
4.1 传统实现方式
在开发文档生成工具时,我们这样构造PDF文档:
public class PdfDocumentBuilder { private String header; private String body; private String footer; public PdfDocumentBuilder setHeader(String header) { this.header = header; return this; } public PdfDocumentBuilder setBody(String body) { this.body = body; return this; } public PdfDocument build() { return new PdfDocument(header, body, footer); } }使用链式调用:
PdfDocument doc = new PdfDocumentBuilder() .setHeader("合同") .setBody("条款内容...") .build();4.2 与工厂模式的区别
关键差异在于:
- 工厂:一次性创建完整对象
- 建造者:分步骤构造复杂对象
我曾用建造者模式重构过一个游戏角色创建系统,将原本需要20个参数的构造函数拆解成了清晰的步骤。
5. 策略模式的实战应用
5.1 电商促销场景
在黑色星期五促销中,我们实现了这样的价格策略系统:
public interface DiscountStrategy { double applyDiscount(double originalPrice); } public class ChristmasDiscount implements DiscountStrategy { public double applyDiscount(double originalPrice) { return originalPrice * 0.7; } } public class PriceCalculator { private DiscountStrategy strategy; public void setStrategy(DiscountStrategy strategy) { this.strategy = strategy; } public double calculate(double price) { return strategy.applyDiscount(price); } }5.2 策略模式+工厂模式
在实际项目中,我常将两者结合使用:
public class StrategyFactory { public static DiscountStrategy createStrategy(String season) { switch (season) { case "christmas": return new ChristmasDiscount(); case "blackfriday": return new BlackFridayDiscount(); default: return new NoDiscount(); } } }这样客户端代码只需:
DiscountStrategy strategy = StrategyFactory.createStrategy("christmas"); calculator.setStrategy(strategy);6. 模板方法模式的框架应用
6.1 支付流程模板
在开发支付网关时,固定流程如下:
public abstract class PaymentProcessor { // 模板方法 public final void processPayment() { validate(); preProcess(); executePayment(); postProcess(); } protected abstract void validate(); protected abstract void executePayment(); protected void preProcess() { // 默认实现:记录日志 } protected void postProcess() { // 默认实现:发送通知 } }6.2 钩子方法的使用技巧
在实现工作流引擎时,我发现可以通过钩子方法增加灵活性:
public abstract class Workflow { public final void execute() { init(); doWork(); if (needAudit()) { audit(); } cleanup(); } protected boolean needAudit() { return true; // 默认需要审核 } }子类可以覆盖needAudit()来改变流程走向。这种技术在Spring的JdbcTemplate中大量使用。
7. 设计模式的组合拳
7.1 Spring中的模式混用
以Spring MVC为例:
- 前端控制器是单例
- 处理器映射使用策略模式
- 视图解析器采用工厂方法
- 拦截器链是责任链模式
7.2 我的架构设计经验
在微服务网关开发中,我这样组合模式:
- 用建造者模式构造请求上下文
- 用责任链模式处理过滤逻辑
- 用策略模式选择路由算法
- 用模板方法定义响应处理流程
关键是要理解每个模式的适用场景,而不是生搬硬套。我见过最糟糕的代码是一个简单工具类里用了5种设计模式,结果没人能维护。
8. 避免设计模式滥用
8.1 反模式案例
曾经review过一个"过度设计"的代码:
- 用抽象工厂创建只有一种实现的DAO
- 用装饰者模式包装没有扩展需求的组件
- 在不必要的场景使用观察者模式
8.2 合理使用原则
我的设计模式决策树:
- 这个变化点真的存在吗?
- 会有多个实现吗?
- 未来扩展的可能性有多大?
- 引入模式的复杂度是否值得?
记住:KISS原则(Keep It Simple, Stupid)永远优先。当你在纠结该用哪种模式时,很可能根本不需要用模式。