Java设计模式实战:单例、工厂方法与抽象工厂的演进与抉择

1. 单例模式:电商系统中的全局配置管理

在电商系统架构演进过程中,单例模式就像一位严谨的仓库管理员,确保全局配置的唯一性。想象你正在开发一个日均PV过百万的中型电商平台,系统中有几十个服务需要读取数据库连接配置、第三方支付密钥等敏感信息。如果每个服务都自己创建配置对象,不仅浪费内存,更可能导致配置不一致引发支付失败等严重问题。

我曾在实际项目中遇到过这样的坑:由于日志服务在多处实例化,导致日志文件被重复写入。后来采用双重检查锁实现单例后,问题迎刃而解。来看这段电商场景的线程安全实现:

public class PaymentConfig { private static volatile PaymentConfig instance; private String apiKey; private String merchantId; private PaymentConfig() { // 初始化支付配置 this.apiKey = loadFromSecureVault(); this.merchantId = "EC001"; } public static PaymentConfig getInstance() { if (instance == null) { synchronized (PaymentConfig.class) { if (instance == null) { instance = new PaymentConfig(); } } } return instance; } }

这种实现方式有三大优势:

  1. 延迟初始化:只有在首次调用getInstance()时才创建对象
  2. 线程安全:通过volatile和synchronized双重保障
  3. 性能优化:后续调用直接返回实例,避免同步开销

但在微服务架构下,单例模式需要调整。当系统拆分为多个服务时,传统的JVM级单例就失效了。这时可以采用:

  • 将配置移至分布式配置中心(如Nacos)
  • 通过@Bean注解在Spring容器中维护单例
  • 使用Redis实现跨进程的单例控制

2. 工厂方法模式:订单处理器的动态创建

当电商系统开始支持多种订单类型(普通订单、预售订单、拼团订单)时,工厂方法模式就像一条智能生产线。以订单处理为例,每种订单的创建逻辑差异很大:

  • 普通订单需要校验库存
  • 预售订单要检查尾款支付时间
  • 拼团订单需验证成团状态

先定义抽象创建器:

public abstract class OrderFactory { public abstract Order createOrder(OrderRequest request); // 公共校验逻辑 protected boolean validateCommon(OrderRequest request) { // 基础校验逻辑... } }

具体工厂实现差异化创建:

public class GroupBuyOrderFactory extends OrderFactory { @Override public Order createOrder(OrderRequest request) { validateCommon(request); // 拼团特有校验 if (!GroupBuyService.isValid(request.getGroupId())) { throw new IllegalStateException("拼团已失效"); } return new GroupBuyOrder(request); } }

工厂方法模式的精妙之处在于:

  1. 符合开闭原则:新增订单类型只需扩展新工厂
  2. 逻辑隔离:每种订单的创建规则封装在专属工厂
  3. 便于测试:可以mock特定工厂进行单元测试

在架构演进过程中,我们发现当订单类型超过10种时,会出现工厂类爆炸的问题。这时可以引入「工厂的工厂」——用Map维护工厂实例:

public class OrderFactoryProducer { private static Map<OrderType, OrderFactory> factories = new EnumMap<>(OrderType.class); static { factories.put(OrderType.NORMAL, new NormalOrderFactory()); factories.put(OrderType.PRESALE, new PresaleOrderFactory()); // ...其他注册 } public static OrderFactory getFactory(OrderType type) { return factories.get(type); } }

3. 抽象工厂模式:跨平台UI组件库设计

当电商业务需要同时支持APP、H5、小程序三端时,抽象工厂就像一位全能设计师。以商品详情页为例,各端的UI组件看似功能相同但实现迥异:

  • APP端使用原生组件
  • H5端使用Vue组件
  • 小程序端使用自定义组件

首先定义抽象组件族:

public interface UIComponentFactory { Header createHeader(); PriceDisplay createPriceDisplay(); SkuSelector createSkuSelector(); }

然后为每个平台创建具体工厂:

public class AppComponentFactory implements UIComponentFactory { @Override public Header createHeader() { return new AppHeader(); } @Override public PriceDisplay createPriceDisplay() { return new AppPriceLabel(); } //...其他组件 }

在渲染引擎中动态切换工厂:

public class DetailPageRenderer { private UIComponentFactory factory; public DetailPageRenderer(Platform platform) { switch(platform) { case APP: factory = new AppComponentFactory(); break; case H5: factory = new H5ComponentFactory(); break; //...其他平台 } } public void render() { Header header = factory.createHeader(); header.render(); //...其他组件 } }

抽象工厂模式在跨平台场景下的优势:

  1. 确保组件兼容性:同一工厂创建的组件保证风格一致
  2. 降低耦合度:业务代码不依赖具体组件实现
  3. 便于扩展:新增平台只需实现新工厂

当遇到需要支持动态主题(如暗黑模式)时,可以将抽象工厂与桥接模式结合,通过组合方式实现多维变化。

4. 模式演进与选型指南

在电商系统从单体到微服务的演进过程中,这三种创建型模式的应用呈现出明显的阶段性特征:

架构阶段典型场景推荐模式注意事项
单体架构配置管理、日志服务单例模式注意线程安全问题
服务化订单多类型处理工厂方法模式防止工厂类膨胀
微服务多端UI统一、跨服务产品族抽象工厂模式配合DI容器使用

选型决策树可以帮助开发者做出合理选择:

  1. 是否需要保证全局唯一实例? → 单例模式
  2. 是否需要创建多种类型的相似对象? → 工厂方法模式
  3. 是否需要创建相关联的对象家族? → 抽象工厂模式

在性能敏感场景下,还需要考虑:

  • 单例模式的双重检查锁性能优于同步方法
  • 工厂方法模式可以配合对象池减少new操作
  • 抽象工厂模式可能增加启动时间,但运行时无负担

我在重构商品搜索系统时,就经历了从混乱的if-else到清晰模式组合的转变。最初直接new各种过滤器,后来改用工厂方法创建不同权重策略,最后用抽象工厂统一管理相关性计算组件,使QPS提升了3倍。