工厂模式:设计与实践

30 阅读11分钟

工厂模式:设计与实践

一、什么是工厂模式

1. 权威定义

根据《设计模式:可复用面向对象软件的基础》(GOF著作)定义:工厂模式(Factory Pattern)是一种创建型设计模式,其核心是定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类

该模式通过封装对象创建逻辑,将实例化过程与使用过程分离,使得系统在无需修改原有代码的情况下,即可新增所需的产品类型。

2. 模式分类

工厂模式包含三种典型实现形式:

  • 简单工厂模式(Simple Factory):由一个工厂类根据参数创建不同类型的产品实例
  • 工厂方法模式(Factory Method):定义抽象工厂接口,由具体工厂子类创建对应产品
  • 抽象工厂模式(Abstract Factory):创建一系列相关或相互依赖的对象族

二、工厂模式的特点

1. 封装对象创建逻辑

将对象的实例化过程封装在工厂类中,客户端无需知道具体的创建细节(如类名、构造参数等),只需通过工厂接口获取产品对象。

2. 基于接口编程

客户端依赖于产品接口和工厂接口,而非具体实现类,符合依赖倒置原则。

3. 延迟实例化

将产品的实例化延迟到具体工厂类中,使得系统可以在运行时动态决定创建哪种产品。

4. 便于扩展

新增产品类型时,只需添加对应的产品类和工厂类,无需修改现有代码,符合开闭原则。

特点说明
封装创建逻辑实例化过程在工厂中完成,客户端无需关注细节
接口导向依赖抽象接口而非具体实现,降低耦合度
延迟实例化运行时动态决定产品类型,提升灵活性
可扩展性新增产品只需扩展工厂和产品类,符合开闭原则

三、工厂模式的标准代码实现

1. 简单工厂模式

// 产品接口
public interface Product {
    void operation();
}

// 具体产品A
public class ConcreteProductA implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductA operation");
    }
}

// 具体产品B
public class ConcreteProductB implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductB operation");
    }
}

// 简单工厂类
public class SimpleFactory {
    public Product createProduct(String type) {
        switch (type) {
            case "A":
                return new ConcreteProductA();
            case "B":
                return new ConcreteProductB();
            default:
                throw new IllegalArgumentException("Invalid product type");
        }
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        SimpleFactory factory = new SimpleFactory();
        Product productA = factory.createProduct("A");
        productA.operation();

        Product productB = factory.createProduct("B");
        productB.operation();
    }
}

2. 工厂方法模式

// 产品接口
public interface Product {
    void operation();
}

// 具体产品A
public class ConcreteProductA implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductA operation");
    }
}

// 具体产品B
public class ConcreteProductB implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductB operation");
    }
}

// 工厂接口
public interface Factory {
    Product createProduct();
}

// 具体工厂A
public class ConcreteFactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

// 具体工厂B
public class ConcreteFactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        Factory factoryA = new ConcreteFactoryA();
        Product productA = factoryA.createProduct();
        productA.operation();

        Factory factoryB = new ConcreteFactoryB();
        Product productB = factoryB.createProduct();
        productB.operation();
    }
}

3. 抽象工厂模式

// 产品族接口A
public interface ProductA {
    void operationA();
}

// 产品族接口B
public interface ProductB {
    void operationB();
}

// 具体产品A1
public class ConcreteProductA1 implements ProductA {
    @Override
    public void operationA() {
        System.out.println("ConcreteProductA1 operation");
    }
}

// 具体产品A2
public class ConcreteProductA2 implements ProductA {
    @Override
    public void operationA() {
        System.out.println("ConcreteProductA2 operation");
    }
}

// 具体产品B1
public class ConcreteProductB1 implements ProductB {
    @Override
    public void operationB() {
        System.out.println("ConcreteProductB1 operation");
    }
}

// 具体产品B2
public class ConcreteProductB2 implements ProductB {
    @Override
    public void operationB() {
        System.out.println("ConcreteProductB2 operation");
    }
}

// 抽象工厂接口
public interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

// 具体工厂1(生产产品族1)
public class ConcreteFactory1 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}

// 具体工厂2(生产产品族2)
public class ConcreteFactory2 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        AbstractFactory factory1 = new ConcreteFactory1();
        ProductA productA1 = factory1.createProductA();
        ProductB productB1 = factory1.createProductB();
        productA1.operationA();
        productB1.operationB();

        AbstractFactory factory2 = new ConcreteFactory2();
        ProductA productA2 = factory2.createProductA();
        ProductB productB2 = factory2.createProductB();
        productA2.operationA();
        productB2.operationB();
    }
}

4. 不同工厂模式特点汇总

维度简单工厂模式工厂方法模式抽象工厂模式
核心角色单一工厂类负责所有产品创建抽象工厂接口+多个具体工厂子类抽象工厂接口+多个产品族工厂
产品类型单一产品接口的不同实现单一产品接口的不同实现多个相关产品接口组成的产品族
创建逻辑基于参数判断创建对应产品每个具体工厂对应一种产品每个工厂创建一整套产品族
客户端依赖依赖工厂类和产品接口依赖抽象工厂和产品接口依赖抽象工厂和产品接口族
新增产品需要修改工厂类的判断逻辑只需新增产品类和对应工厂类新增产品族需修改抽象工厂接口
灵活性低(耦合度高)中(符合开闭原则)高(适合产品族场景)
实现复杂度简单中等(类数量增加)复杂(需管理多个产品接口)
典型应用场景产品类型少且固定的场景产品类型多变但单一产品线需要统一风格的系列产品场景

四、支付框架设计中工厂模式的运用

以支付订单工厂为例,说明工厂模式在支付框架中的具体实现:

1. 场景分析

支付系统需要处理多种订单类型(普通支付单、预授权单、分账单等),不同订单的创建逻辑、字段验证和存储方式存在差异,但需统一纳入订单生命周期管理。

2. 设计实现

2.1 抽象订单基类
import java.math.BigDecimal;
import java.time.LocalDateTime;

public abstract class PaymentOrder {
    protected String orderId;
    protected String merchantId;
    protected BigDecimal amount;
    protected String currency;
    protected String status;
    protected LocalDateTime createTime;
    protected LocalDateTime updateTime;

    public abstract void validate();
    public abstract void save();

    // 通用getter和setter
    public String getOrderId() { return orderId; }
    public void setOrderId(String orderId) { this.orderId = orderId; }
    public String getMerchantId() { return merchantId; }
    public void setMerchantId(String merchantId) { this.merchantId = merchantId; }
    public BigDecimal getAmount() { return amount; }
    public void setAmount(BigDecimal amount) { this.amount = amount; }
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
}
2.2 具体订单实现
// 普通支付订单
public class NormalPaymentOrder extends PaymentOrder {
    private String goodsId;
    private String goodsName;
    private int quantity;

    @Override
    public void validate() {
        if (goodsId == null || goodsId.isEmpty()) {
            throw new IllegalArgumentException("商品ID不能为空");
        }
        if (quantity <= 0) {
            throw new IllegalArgumentException("商品数量必须大于0");
        }
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("订单金额必须大于0");
        }
    }

    @Override
    public void save() {
        // 保存到普通订单表
        System.out.println("保存普通支付订单: " + orderId);
        // 实际实现中会调用DAO层插入数据库
    }

    // 特有字段的getter和setter
    public String getGoodsId() { return goodsId; }
    public void setGoodsId(String goodsId) { this.goodsId = goodsId; }
    public String getGoodsName() { return goodsName; }
    public void setGoodsName(String goodsName) { this.goodsName = goodsName; }
    public int getQuantity() { return quantity; }
    public void setQuantity(int quantity) { this.quantity = quantity; }
}

// 预授权订单
public class PreAuthorizationOrder extends PaymentOrder {
    private int authExpireHours;
    private String authCode;
    private BigDecimal preAuthAmount;

    @Override
    public void validate() {
        if (authExpireHours <= 0) {
            throw new IllegalArgumentException("预授权有效期必须大于0");
        }
        if (preAuthAmount == null || preAuthAmount.compareTo(amount) < 0) {
            throw new IllegalArgumentException("预授权金额不能小于订单金额");
        }
    }

    @Override
    public void save() {
        // 保存到预授权订单表
        System.out.println("保存预授权订单: " + orderId);
        // 实际实现中会调用DAO层插入数据库
    }

    // 特有字段的getter和setter
    public int getAuthExpireHours() { return authExpireHours; }
    public void setAuthExpireHours(int authExpireHours) { this.authExpireHours = authExpireHours; }
    public String getAuthCode() { return authCode; }
    public void setAuthCode(String authCode) { this.authCode = authCode; }
    public BigDecimal getPreAuthAmount() { return preAuthAmount; }
    public void setPreAuthAmount(BigDecimal preAuthAmount) { this.preAuthAmount = preAuthAmount; }
}

// 分账单
public class SplitPaymentOrder extends PaymentOrder {
    private String parentOrderId;
    private int splitCount;
    private BigDecimal serviceFee;

    @Override
    public void validate() {
        if (parentOrderId == null || parentOrderId.isEmpty()) {
            throw new IllegalArgumentException("父订单ID不能为空");
        }
        if (splitCount <= 0) {
            throw new IllegalArgumentException("分账数量必须大于0");
        }
        if (serviceFee == null) {
            throw new IllegalArgumentException("服务费不能为空");
        }
    }

    @Override
    public void save() {
        // 保存到分账单表
        System.out.println("保存分账单: " + orderId);
        // 实际实现中会调用DAO层插入数据库
    }

    // 特有字段的getter和setter
    public String getParentOrderId() { return parentOrderId; }
    public void setParentOrderId(String parentOrderId) { this.parentOrderId = parentOrderId; }
    public int getSplitCount() { return splitCount; }
    public void setSplitCount(int splitCount) { this.splitCount = splitCount; }
    public BigDecimal getServiceFee() { return serviceFee; }
    public void setServiceFee(BigDecimal serviceFee) { this.serviceFee = serviceFee; }
}
2.3 订单工厂实现
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Random;

public class PaymentOrderFactory {
    private static final String NORMAL_ORDER_TYPE = "NORMAL";
    private static final String PRE_AUTH_ORDER_TYPE = "PRE_AUTH";
    private static final String SPLIT_ORDER_TYPE = "SPLIT";
    private static final Random RANDOM = new Random();

    public static PaymentOrder createOrder(String orderType, Map<String, Object> params) {
        PaymentOrder order;
        switch (orderType) {
            case NORMAL_ORDER_TYPE:
                order = createNormalOrder(params);
                break;
            case PRE_AUTH_ORDER_TYPE:
                order = createPreAuthOrder(params);
                break;
            case SPLIT_ORDER_TYPE:
                order = createSplitOrder(params);
                break;
            default:
                throw new IllegalArgumentException("不支持的订单类型: " + orderType);
        }

        // 设置通用字段
        order.setOrderId(generateOrderId(orderType));
        order.setStatus("INIT");
        order.setCreateTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());
        order.setMerchantId((String) params.get("merchantId"));
        order.setCurrency(params.getOrDefault("currency", "CNY").toString());

        return order;
    }

    private static NormalPaymentOrder createNormalOrder(Map<String, Object> params) {
        NormalPaymentOrder order = new NormalPaymentOrder();
        order.setAmount(new BigDecimal(params.get("amount").toString()));
        order.setGoodsId((String) params.get("goodsId"));
        order.setGoodsName((String) params.get("goodsName"));
        order.setQuantity(Integer.parseInt(params.get("quantity").toString()));
        return order;
    }

    private static PreAuthorizationOrder createPreAuthOrder(Map<String, Object> params) {
        PreAuthorizationOrder order = new PreAuthorizationOrder();
        order.setAmount(new BigDecimal(params.get("amount").toString()));
        order.setPreAuthAmount(new BigDecimal(params.get("preAuthAmount").toString()));
        order.setAuthExpireHours(Integer.parseInt(params.get("authExpireHours").toString()));
        order.setAuthCode((String) params.get("authCode"));
        return order;
    }

    private static SplitPaymentOrder createSplitOrder(Map<String, Object> params) {
        SplitPaymentOrder order = new SplitPaymentOrder();
        order.setAmount(new BigDecimal(params.get("amount").toString()));
        order.setParentOrderId((String) params.get("parentOrderId"));
        order.setSplitCount(Integer.parseInt(params.get("splitCount").toString()));
        order.setServiceFee(new BigDecimal(params.get("serviceFee").toString()));
        return order;
    }

    private static String generateOrderId(String orderType) {
        String prefix = orderType.substring(0, 2).toUpperCase();
        LocalDateTime now = LocalDateTime.now();
        return String.format("%s%s%04d", 
            prefix,
            now.format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss")),
            RANDOM.nextInt(10000));
    }
}
2.4 客户端使用
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

public class OrderService {
    public String createPaymentOrder(String orderType, Map<String, Object> params) {
        // 通过工厂创建订单
        PaymentOrder order = PaymentOrderFactory.createOrder(orderType, params);

        // 执行订单验证和保存
        order.validate();
        order.save();

        return order.getOrderId();
    }

    public static void main(String[] args) {
        OrderService service = new OrderService();

        // 创建普通支付订单
        Map<String, Object> normalParams = new HashMap<>();
        normalParams.put("merchantId", "MCH001");
        normalParams.put("amount", "99.99");
        normalParams.put("goodsId", "G001");
        normalParams.put("goodsName", "测试商品");
        normalParams.put("quantity", "1");
        String normalOrderId = service.createPaymentOrder("NORMAL", normalParams);
        System.out.println("创建普通订单成功: " + normalOrderId);

        // 创建预授权订单
        Map<String, Object> preAuthParams = new HashMap<>();
        preAuthParams.put("merchantId", "MCH001");
        preAuthParams.put("amount", "1000.00");
        preAuthParams.put("preAuthAmount", "1200.00");
        preAuthParams.put("authExpireHours", "24");
        preAuthParams.put("authCode", "AUTH123456");
        String preAuthOrderId = service.createPaymentOrder("PRE_AUTH", preAuthParams);
        System.out.println("创建预授权订单成功: " + preAuthOrderId);
    }
}

3. 实现优势

  1. 集中管理创建逻辑:所有订单的创建逻辑集中在工厂类中,避免在业务代码中散落重复的初始化代码。
  2. 标准化订单流程:确保同类订单遵循相同的创建和初始化流程,减少因手工创建导致的字段遗漏或错误。
  3. 简化业务代码:客户端只需调用工厂方法并传入参数,无需关心具体订单类型的实现细节。
  4. 便于扩展:新增订单类型时,只需添加新的订单类并扩展工厂方法,无需修改现有业务逻辑。

五、开源框架中工厂模式的运用(MyBatis)

MyBatis框架广泛使用了工厂模式,其中SqlSessionFactory是典型代表,采用了工厂方法模式的设计思想。

1. 核心实现分析

1.1 工厂接口定义
// SqlSession工厂接口
public interface SqlSessionFactory {
    SqlSession openSession();
    SqlSession openSession(boolean autoCommit);
    SqlSession openSession(Connection connection);
    SqlSession openSession(TransactionIsolationLevel level);
    SqlSession openSession(ExecutorType execType);
    SqlSession openSession(ExecutorType execType, boolean autoCommit);
    SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
    SqlSession openSession(ExecutorType execType, Connection connection);

    Configuration getConfiguration();
}
1.2 具体工厂实现

MyBatis提供了DefaultSqlSessionFactory作为SqlSessionFactory的默认实现:

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }

    @Override
    public SqlSession openSession(boolean autoCommit) {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
    }

    // 其他openSession方法实现...

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
            final Environment environment = configuration.getEnvironment();
            final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            final Executor executor = configuration.newExecutor(tx, execType);
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            closeTransaction(tx); // may have fetched a connection so lets call close()
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }
}
1.3 工厂构建过程

SqlSessionFactory通过SqlSessionFactoryBuilder创建,这是典型的建造者模式与工厂模式结合:

public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
    }

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}
1.4 客户端使用方式
// 构建SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 使用工厂创建SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.selectById(1);
}

2. 工厂模式在MyBatis中的作用

  1. 封装会话创建逻辑SqlSessionFactory封装了SqlSession的创建细节,包括事务管理、执行器类型、数据源等配置。
  2. 提供统一接口:客户端通过SqlSessionFactory的标准化方法获取SqlSession,无需关心底层实现差异。
  3. 支持多种配置方式:通过不同的openSession()方法重载,支持事务隔离级别、执行器类型等多种参数配置。
  4. 便于扩展:可以通过实现SqlSessionFactory接口自定义会话工厂,满足特殊需求。

六、总结

1. 工厂模式的适用场景

  • 当系统需要创建多种相关或相似的对象,且客户端无需知道具体创建细节时
  • 当需要动态决定创建哪种产品对象,或需要在运行时切换产品类型时
  • 当产品类的构造过程复杂,或需要进行额外初始化操作时
  • 当需要统一管理对象的创建和生命周期时

2. 三种工厂模式的选择建议

模式类型适用场景优点缺点
简单工厂产品类型较少且固定的场景实现简单,客户端使用方便新增产品需修改工厂类,违背开闭原则
工厂方法产品类型较多且可能扩展的场景符合开闭原则,扩展性好类数量增多,系统复杂度提高
抽象工厂需要创建产品族或对象系列的场景便于管理相关产品族,一致性好扩展新的产品族困难,需要修改抽象工厂接口

3. 工厂模式在支付系统中的价值

  • 标准化对象创建:统一支付相关对象(订单、渠道、通知等)的创建流程,确保系统一致性
  • 降低耦合度:客户端与具体实现类解耦,提高系统的可维护性和可测试性
  • 提升扩展性:新增支付渠道、订单类型时,只需扩展工厂和产品类,不影响现有业务
  • 便于管理复杂对象:封装支付对象的复杂创建逻辑(如签名、加密、参数校验等)

工厂模式通过将对象创建与使用分离,为支付系统提供了灵活的扩展机制和清晰的架构设计,是构建可扩展、可维护支付框架的重要模式选择。