☰
简单工厂模式解析:支付系统实战与设计模式基础
2026/10/31 15:58:37 网站建设 项目流程

1. 简单工厂模式概述

简单工厂模式(Simple Factory Pattern)是设计模式中最基础的一种创建型模式。它通过一个工厂类来封装对象的创建过程,客户端无需知道具体类的名称,只需要知道工厂类即可获取所需对象。这种模式在需要根据条件动态创建不同类实例的场景中尤为实用。

我在实际项目中最常遇到的应用场景是支付系统。当系统需要支持微信支付、支付宝支付、银联支付等多种支付方式时,简单工厂模式可以优雅地解决支付对象创建的问题。客户端只需要传递"wechat"、"alipay"等参数,工厂就能返回对应的支付处理器实例。

2. 模式结构与核心组件

2.1 UML类图解析

简单工厂模式包含三个核心角色:

  1. 工厂类(Factory):负责创建具体产品对象的中心枢纽,包含创建对象的静态方法
  2. 抽象产品(Product):定义产品对象的公共接口
  3. 具体产品(ConcreteProduct):实现抽象产品接口的具体类
classDiagram class Factory { +createProduct(type:String): Product } class Product { <<interface>> +use(): void } class ConcreteProductA { +use(): void } class ConcreteProductB { +use(): void } Factory --> Product Product <|-- ConcreteProductA Product <|-- ConcreteProductB

2.2 代码实现示例

以支付系统为例,我们来看一个典型实现:

// 抽象产品 public interface Payment { void pay(BigDecimal amount); } // 具体产品 public class WechatPay implements Payment { @Override public void pay(BigDecimal amount) { System.out.println("使用微信支付:" + amount); } } public class Alipay implements Payment { @Override public void pay(BigDecimal amount) { System.out.println("使用支付宝支付:" + amount); } } // 工厂类 public class PaymentFactory { public static Payment createPayment(String type) { switch (type.toLowerCase()) { case "wechat": return new WechatPay(); case "alipay": return new Alipay(); default: throw new IllegalArgumentException("不支持的支付类型"); } } } // 客户端调用 public class Client { public static void main(String[] args) { Payment payment = PaymentFactory.createPayment("wechat"); payment.pay(new BigDecimal("100.00")); } }

3. 模式优势与适用场景

3.1 核心优势分析

  1. 解耦客户端与具体实现:客户端只需要知道工厂类和抽象产品接口,无需关心具体实现类
  2. 集中化管理对象创建:所有创建逻辑集中在工厂类中,便于统一管理和修改
  3. 降低系统复杂度:客户端代码变得更简洁,不需要包含大量条件判断逻辑

3.2 典型应用场景

  1. 支付系统:如前面示例所示的各种支付方式
  2. 日志系统:创建文件日志、数据库日志等不同日志处理器
  3. 数据库连接:创建MySQL、Oracle等不同数据库连接
  4. UI组件库:根据不同平台创建对应的UI控件
  5. 游戏开发:根据角色类型创建不同的角色对象

提示:当系统中需要创建的对象类型较少且相对固定时,简单工厂模式是最佳选择。如果类型经常变化或数量庞大,考虑使用工厂方法模式。

4. 实现细节与最佳实践

4.1 工厂方法的三种实现方式

  1. 静态工厂方法(最常见)
public static Product createProduct(String type) { // 创建逻辑 }
  1. 多方法工厂(避免类型字符串错误)
public static Product createProductA() { return new ConcreteProductA(); } public static Product createProductB() { return new ConcreteProductB(); }
  1. 参数化工厂(使用枚举更安全)
public enum ProductType { TYPE_A, TYPE_B } public static Product createProduct(ProductType type) { switch (type) { case TYPE_A: return new ProductA(); case TYPE_B: return new ProductB(); } }

4.2 设计注意事项

  1. 开闭原则的权衡:新增产品类型需要修改工厂类,这违反了开闭原则。这是简单工厂模式的主要局限。

  2. 工厂类的单例化:通常工厂类不需要维护状态,可以设计为单例或直接使用静态方法。

  3. 异常处理:当传入无效参数时,应该抛出明确的异常而非返回null。

  4. 命名规范:工厂方法名称通常使用"create"前缀,如createInstance、createProduct等。

5. 模式变体与扩展

5.1 带缓存的简单工厂

对于创建成本较高的对象,可以在工厂中引入缓存机制:

public class CachedFactory { private static final Map<String, Product> cache = new HashMap<>(); public static Product createProduct(String type) { if (cache.containsKey(type)) { return cache.get(type); } Product product; switch (type) { case "A": product = new ProductA(); break; case "B": product = new ProductB(); break; default: throw new IllegalArgumentException(); } cache.put(type, product); return product; } }

5.2 结合依赖注入

在现代框架中,简单工厂常与依赖注入结合使用:

@Configuration public class AppConfig { @Bean @Scope("prototype") public Payment wechatPay() { return new WechatPay(); } @Bean @Scope("prototype") public Payment alipay() { return new Alipay(); } } @Service public class PaymentService { private final PaymentFactory factory; @Autowired public PaymentService(PaymentFactory factory) { this.factory = factory; } public void processPayment(String type) { Payment payment = factory.createPayment(type); payment.pay(amount); } }

6. 常见问题与解决方案

6.1 问题1:如何避免修改工厂类?

场景:每次新增产品类型都需要修改工厂类的switch语句。

解决方案:

  1. 使用反射机制动态加载类
  2. 配合配置文件(如properties、XML)定义类型映射
  3. 使用服务发现机制(如Java的ServiceLoader)

示例:基于反射的实现

public class ReflectionFactory { private static final String PACKAGE = "com.example.products."; public static Product createProduct(String type) { try { Class<?> clazz = Class.forName(PACKAGE + type); return (Product) clazz.newInstance(); } catch (Exception e) { throw new RuntimeException("创建产品失败", e); } } }

6.2 问题2:如何处理产品初始化参数?

场景:不同产品需要不同的初始化参数。

解决方案:

  1. 使用Builder模式配合工厂
  2. 定义统一的初始化接口
  3. 使用参数对象封装所有可能参数

示例:参数对象方案

public class ProductParams { private String configFile; private int timeout; // 其他参数... // getters/setters } public interface Product { void init(ProductParams params); } public class ProductFactory { public static Product createProduct(String type, ProductParams params) { Product product; switch (type) { case "A": product = new ProductA(); break; case "B": product = new ProductB(); break; default: throw new IllegalArgumentException(); } product.init(params); return product; } }

7. 性能优化与线程安全

7.1 对象池技术

对于创建成本高且可复用的对象,可以结合对象池:

public class ProductPool { private static final Map<String, BlockingQueue<Product>> pools = new ConcurrentHashMap<>(); public static Product getProduct(String type) { pools.putIfAbsent(type, new LinkedBlockingQueue<>(10)); BlockingQueue<Product> pool = pools.get(type); Product product = pool.poll(); if (product == null) { product = ProductFactory.createProduct(type); } return product; } public static void returnProduct(String type, Product product) { if (product != null) { pools.get(type).offer(product); } } }

7.2 线程安全考量

  1. 无状态工厂:静态工厂方法通常是线程安全的
  2. 有状态工厂:需要同步机制保护共享状态
  3. 双重检查锁定:适用于延迟初始化的场景

示例:线程安全的延迟初始化

public class SingletonFactory { private static volatile Product instance; public static Product getInstance() { if (instance == null) { synchronized (SingletonFactory.class) { if (instance == null) { instance = createProduct(); } } } return instance; } private static Product createProduct() { // 实际创建逻辑 } }

8. 测试策略与Mock应用

8.1 工厂类的单元测试

public class PaymentFactoryTest { @Test public void testCreateWechatPay() { Payment payment = PaymentFactory.createPayment("wechat"); assertTrue(payment instanceof WechatPay); } @Test(expected = IllegalArgumentException.class) public void testInvalidType() { PaymentFactory.createPayment("invalid"); } }

8.2 使用Mock对象测试客户端

public class ClientTest { @Test public void testClientWithMock() { // 创建mock产品 Payment mockPayment = mock(Payment.class); // 创建mock工厂 PaymentFactory factory = mock(PaymentFactory.class); when(factory.createPayment(anyString())).thenReturn(mockPayment); // 测试客户端 Client client = new Client(factory); client.processPayment(); verify(mockPayment).pay(any(BigDecimal.class)); } }

9. 与其他模式的比较与结合

9.1 与工厂方法模式对比

特性简单工厂模式工厂方法模式
创建逻辑集中度完全集中在一个工厂类分散在各个具体工厂类
扩展性需要修改工厂类代码可以新增工厂类无需修改现有代码
复杂度简单直观类数量增加,结构更复杂
适用场景产品类型少且稳定产品类型多且可能频繁变化

9.2 与抽象工厂模式结合

简单工厂可以作为抽象工厂的基础构建块:

public interface GUIFactory { Button createButton(); Menu createMenu(); } public class WindowsFactory implements GUIFactory { @Override public Button createButton() { return SimpleButtonFactory.createButton("windows"); } @Override public Menu createMenu() { return SimpleMenuFactory.createMenu("windows"); } }

10. 实际项目经验分享

在电商平台的优惠券系统中,我们使用简单工厂模式实现了多种优惠券类型的创建:

public interface Coupon { BigDecimal applyDiscount(BigDecimal originalPrice); } public class PercentageCoupon implements Coupon { private final BigDecimal percentage; public PercentageCoupon(BigDecimal percentage) { this.percentage = percentage; } @Override public BigDecimal applyDiscount(BigDecimal originalPrice) { return originalPrice.multiply(percentage); } } public class FixedAmountCoupon implements Coupon { private final BigDecimal amount; public FixedAmountCoupon(BigDecimal amount) { this.amount = amount; } @Override public BigDecimal applyDiscount(BigDecimal originalPrice) { return originalPrice.subtract(amount); } } public class CouponFactory { public static Coupon createCoupon(String type, BigDecimal value) { switch (type.toLowerCase()) { case "percentage": return new PercentageCoupon(value); case "fixed": return new FixedAmountCoupon(value); default: throw new IllegalArgumentException("不支持的优惠券类型"); } } }

踩坑经验:

  1. 最初没有对value参数进行有效性验证,导致出现负折扣问题
  2. 优惠券类型使用字符串常量,后来改用枚举更安全
  3. 没有考虑线程安全问题,在高并发场景下出现对象状态混乱

优化后的实现:

public enum CouponType { PERCENTAGE, FIXED_AMOUNT } public class SafeCouponFactory { private static final Map<CouponType, Function<BigDecimal, Coupon>> creators = Map.of( CouponType.PERCENTAGE, PercentageCoupon::new, CouponType.FIXED_AMOUNT, FixedAmountCoupon::new ); public static Coupon createCoupon(CouponType type, BigDecimal value) { if (value.compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgumentException("折扣值必须大于0"); } Function<BigDecimal, Coupon> creator = creators.get(type); if (creator == null) { throw new IllegalArgumentException("不支持的优惠券类型"); } return creator.apply(value); } }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询