ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

设计模式实战:单例、工厂与策略模式解析

设计模式实战:单例、工厂与策略模式解析

1. 设计模式为何值得速通?

设计模式就像武侠小说中的招式套路,是前人总结出来的最佳实践。我第一次接触设计模式是在2015年参与一个电商系统重构时,当时面对错综复杂的订单处理逻辑,我的导师指着代码说:"这里该用策略模式了"。那次经历让我明白,设计模式不是纸上谈兵的理论,而是解决实际问题的利器。

为什么设计模式能成为面试必问、项目必备的技能?因为它们解决了软件开发中的三类核心问题:

  1. 对象创建难题:如何优雅地创建对象?单例确保全局唯一,工厂隐藏创建细节,建造者分步构造复杂对象。

  2. 行为组织难题:如何灵活定义算法族?策略模式让算法可互换,模板方法固定流程骨架。

  3. 结构优化难题:如何扩展功能而不改原有代码?装饰者动态添加职责,适配器兼容不同接口。

提示:设计模式不是银弹,过度使用会导致代码过度设计。我的经验法则是:当你在第三次写相似结构的代码时,才考虑引入设计模式。

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 我的架构设计经验

在微服务网关开发中,我这样组合模式:

  1. 用建造者模式构造请求上下文
  2. 用责任链模式处理过滤逻辑
  3. 用策略模式选择路由算法
  4. 用模板方法定义响应处理流程

关键是要理解每个模式的适用场景,而不是生搬硬套。我见过最糟糕的代码是一个简单工具类里用了5种设计模式,结果没人能维护。

8. 避免设计模式滥用

8.1 反模式案例

曾经review过一个"过度设计"的代码:

  • 用抽象工厂创建只有一种实现的DAO
  • 用装饰者模式包装没有扩展需求的组件
  • 在不必要的场景使用观察者模式

8.2 合理使用原则

我的设计模式决策树:

  1. 这个变化点真的存在吗?
  2. 会有多个实现吗?
  3. 未来扩展的可能性有多大?
  4. 引入模式的复杂度是否值得?

记住:KISS原则(Keep It Simple, Stupid)永远优先。当你在纠结该用哪种模式时,很可能根本不需要用模式。

返回列表