0
点赞
收藏
分享

微信扫一扫

Java 设计模式:工厂模式在框架中的应用

ZSACH 4小时前 阅读 1

在Java开发中,设计模式常常被认为是"理论大于实践"的知识——书本上的示例大多是"创建汽车" "生产电脑"这类简单场景,让人疑惑这些模式在实际框架中到底怎么用。但如果你仔细研究Spring、MyBatis等主流框架的源码,会发现设计模式无处不在,尤其是工厂模式,几乎成了框架设计的"基础设施"。

曾经在维护一个老项目时,遇到过这样的问题:系统需要对接多个支付渠道(支付宝、微信、银联),最初的代码用了大量if-else判断渠道类型,每次新增渠道都要修改核心逻辑,稍不注意就会影响现有功能。后来用工厂模式重构后,新增渠道只需添加一个实现类和配置,无需修改原有代码,稳定性和扩展性提升明显。这个经历让我意识到,工厂模式的核心价值不在于"创建对象",而在于解耦对象创建与使用,这也是它被框架广泛采用的根本原因。

一、工厂模式的三种形态与框架实践

工厂模式主要有三种形态:简单工厂、工厂方法和抽象工厂。它们并非相互替代,而是分别适用于不同复杂度的场景。

1. 简单工厂:框架中的参数化创建

简单工厂通过一个统一的工厂类,根据参数创建不同类型的对象。这种模式在框架中常用于"根据配置创建实例"的场景,比如日志框架SLF4J对日志实现类的选择。

SLF4J中的简单工厂示例

// SLF4J获取Logger的核心逻辑(简化版)
public class LoggerFactory {
    // 根据类名获取Logger实例
    public static Logger getLogger(String name) {
        // 实际逻辑会根据配置的日志实现(Logback/Log4j等)创建对应的Logger
        ILoggerFactory factory = getILoggerFactory();
        return factory.getLogger(name);
    }
    
    // 获取日志工厂实例(简单工厂的核心)
    private static ILoggerFactory getILoggerFactory() {
        // 检查是否有Logback的实现
        if (isLogbackAvailable()) {
            return new LogbackLoggerFactory();
        }
        // 检查是否有Log4j的实现
        else if (isLog4jAvailable()) {
            return new Log4jLoggerFactory();
        }
        // 默认使用JDK自带日志
        else {
            return new JDK14LoggerFactory();
        }
    }
}

这里的LoggerFactory就是典型的简单工厂:它根据classpath中存在的日志实现类,自动创建对应的Logger实例,用户无需关心具体实现,只需调用LoggerFactory.getLogger()即可。

2. 工厂方法:Spring中的Bean创建

工厂方法将对象创建延迟到子类,每个产品对应一个工厂。这种模式在Spring中应用广泛,最典型的就是FactoryBean接口——它允许开发者自定义Bean的创建逻辑。

Spring中FactoryBean的应用

// 自定义FactoryBean,创建RedisTemplate实例
public class RedisTemplateFactoryBean implements FactoryBean<RedisTemplate> {
    private String host;
    private int port;
    
    // 设置Redis连接参数(通过Spring配置注入)
    public void setHost(String host) { this.host = host; }
    public void setPort(int port) { this.port = port; }
    
    // 工厂方法:创建并配置RedisTemplate
    @Override
    public RedisTemplate getObject() throws Exception {
        RedisTemplate template = new RedisTemplate();
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName(host);
        factory.setPort(port);
        factory.afterPropertiesSet(); // 初始化连接工厂
        template.setConnectionFactory(factory);
        template.afterPropertiesSet(); // 初始化RedisTemplate
        return template;
    }
    
    // 返回产品类型
    @Override
    public Class<?> getObjectType() {
        return RedisTemplate.class;
    }
}

// Spring配置文件中使用工厂Bean
<bean id="redisTemplate" class="com.example.RedisTemplateFactoryBean">
    <property name="host" value="localhost"/>
    <property name="port" value="6379"/>
</bean>

通过FactoryBean,开发者可以将复杂的对象创建逻辑封装在工厂中,Spring容器只需调用getObject()即可获取实例。这种方式比直接通过构造器创建Bean更灵活,尤其适合需要大量初始化操作的对象。

3. 抽象工厂:MyBatis的数据源适配

抽象工厂用于创建一系列相关或相互依赖的对象,比如不同数据库(MySQL、Oracle)的连接池、语句对象等。MyBatis的DataSource模块就使用了抽象工厂模式,适配不同的数据库环境。

MyBatis中的抽象工厂简化示例

// 抽象产品:数据库连接
public interface Connection {
    void execute(String sql);
}

// 具体产品:MySQL连接
public class MySQLConnection implements Connection {
    @Override
    public void execute(String sql) {
        System.out.println("MySQL执行SQL:" + sql);
    }
}

// 具体产品:Oracle连接
public class OracleConnection implements Connection {
    @Override
    public void execute(String sql) {
        System.out.println("Oracle执行SQL:" + sql);
    }
}

// 抽象工厂:数据库产品族工厂
public interface DatabaseFactory {
    Connection createConnection();
    Statement createStatement(); // 其他相关产品
}

// 具体工厂:MySQL工厂
public class MySQLFactory implements DatabaseFactory {
    @Override
    public Connection createConnection() {
        return new MySQLConnection();
    }
    @Override
    public Statement createStatement() {
        return new MySQLStatement();
    }
}

// 具体工厂:Oracle工厂
public class OracleFactory implements DatabaseFactory {
    @Override
    public Connection createConnection() {
        return new OracleConnection();
    }
    @Override
    public Statement createStatement() {
        return new OracleStatement();
    }
}

// MyBatis中根据配置选择工厂
public class DatabaseFactoryBuilder {
    public static DatabaseFactory build(String type) {
        if ("mysql".equals(type)) {
            return new MySQLFactory();
        } else if ("oracle".equals(type)) {
            return new OracleFactory();
        } else {
            throw new IllegalArgumentException("不支持的数据库类型");
        }
    }
}

MyBatis通过这种方式,让用户只需配置数据库类型,就能自动获取对应的连接、语句等对象,无需关心不同数据库的实现差异。

二、工厂模式解决的核心问题

框架设计中,工厂模式主要解决三类问题:

  1. 屏蔽实现细节
    用户只需知道"要什么",无需知道"怎么创建"。比如Spring的BeanFactory,用户通过getBean()获取对象,无需关心Bean的初始化过程。
  2. 应对变化扩展
    新增产品时无需修改原有代码。比如Slf4j要支持新的日志框架,只需新增对应的ILoggerFactory实现,无需修改LoggerFactory
  3. 管理对象生命周期
    工厂可以统一管理对象的创建、初始化和销毁。比如Spring的FactoryBean,可以在getObject()中处理初始化逻辑,在destroy()中释放资源。

三、自己实现工厂模式的最佳实践

在实际开发中,我们可以借鉴框架的设计思路,合理使用工厂模式:

1. 简单场景用静态工厂

当产品类型较少且变化不频繁时,用静态工厂更简洁:

// 支付渠道静态工厂
public class PaymentFactory {
    // 私有构造器,防止实例化
    private PaymentFactory() {}
    
    // 根据渠道类型创建支付实例
    public static Payment createPayment(String channel) {
        switch (channel) {
            case "alipay":
                return new AlipayPayment();
            case "wechat":
                return new WechatPayment();
            case "unionpay":
                return new UnionpayPayment();
            default:
                throw new IllegalArgumentException("不支持的支付渠道:" + channel);
        }
    }
}

// 使用方式
Payment payment = PaymentFactory.createPayment("alipay");
payment.pay(new BigDecimal("100"));

2. 复杂场景用工厂接口+配置

当产品类型较多或需要动态扩展时,结合接口和配置文件(或注解)更灵活:

// 支付渠道工厂接口
public interface PaymentFactory {
    Payment create();
    String getChannel(); // 返回支持的渠道
}

// 支付宝工厂实现
@Component
public class AlipayFactory implements PaymentFactory {
    @Override
    public Payment create() {
        return new AlipayPayment();
    }
    @Override
    public String getChannel() {
        return "alipay";
    }
}

// 支付工厂管理器(自动发现所有工厂)
@Component
public class PaymentFactoryManager {
    private final Map<String, PaymentFactory> factories = new HashMap<>();
    
    // Spring自动注入所有PaymentFactory实现
    public PaymentFactoryManager(List<PaymentFactory> factoryList) {
        for (PaymentFactory factory : factoryList) {
            factories.put(factory.getChannel(), factory);
        }
    }
    
    // 根据渠道获取支付实例
    public Payment getPayment(String channel) {
        PaymentFactory factory = factories.get(channel);
        if (factory == null) {
            throw new IllegalArgumentException("不支持的支付渠道:" + channel);
        }
        return factory.create();
    }
}

这种方式下,新增支付渠道只需添加PaymentFactory实现类并标注@Component,无需修改现有代码,完全符合"开闭原则"。

四、工厂模式的使用误区

  1. 过度设计
    不要为简单的对象创建强行引入工厂模式。如果一个对象通过new关键字就能轻松创建,且未来不会有变化,直接实例化更合适。
  2. 工厂职责过重
    避免在工厂中加入复杂的业务逻辑。工厂的核心职责是"创建对象",业务逻辑应放在产品类或其他服务中。
  3. 忽略默认实现
    框架通常会提供默认工厂实现(如Spring的DefaultListableBeanFactory),大多数情况下,直接使用或扩展默认实现比从零开始更高效。

总结

工厂模式在框架中的应用,本质上是对"对象创建"这一行为的规范化和抽象化。从Spring的BeanFactory到MyBatis的数据源工厂,再到SLF4J的日志工厂,它们都遵循同一个设计理念:将对象的创建与使用分离,让系统更灵活、更易于扩展

在实际开发中,我们不必拘泥于工厂模式的三种形态,而应理解其"解耦"的核心思想。当发现代码中出现大量new关键字、if-else类型判断,或者需要频繁新增相似类型的对象时,不妨考虑用工厂模式重构——就像那些优秀的框架一样,让代码在变化中保持稳定与优雅。

举报

相关推荐

0 条评论