工厂模式详解

深入理解设计模式之工厂方法模式(Factory Method Pattern)

一、引言

在软件开发中,对象创建是一个非常常见的操作。但是,如果在代码中直接使用new操作符创建对象,会导致代码与具体类紧密耦合,降低系统的灵活性和可扩展性。工厂方法模式正是为了解决这个问题而诞生的一种创建型设计模式。

本文将深入探讨工厂方法模式的原理、实现方式,并结合实际生产场景和开源框架(如JDK、Spring、Guava)中的应用,帮助你全面掌握这一重要的设计模式。

二、什么是工厂方法模式

2.1 定义

工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。

2.2 核心思想

  • 封装对象创建:将对象的创建逻辑封装在工厂方法中
  • 依赖抽象而非具体:客户端依赖抽象产品和抽象工厂
  • 开闭原则:添加新产品时无需修改现有代码
  • 单一职责:将产品创建职责分离到专门的工厂类

2.3 模式结构

scss 复制代码
┌──────────────────────┐
│    <<interface>>     │
│      Product         │    抽象产品
├──────────────────────┤
│ + operation()        │
└──────────────────────┘
           △
           │ 实现
     ┌─────┴──────┐
     │            │
┌────┴─────┐  ┌──┴────────┐
│ConcreteA │  │ConcreteB  │  具体产品
└──────────┘  └───────────┘
     △              △
     │创建          │创建
     │              │
┌────┴─────────────┴────┐
│   <<abstract>>         │
│      Creator           │    抽象创建者
├────────────────────────┤
│ + factoryMethod()      │ <-- 工厂方法(抽象)
│ + operation()          │ <-- 使用产品的业务方法
└────────────────────────┘
           △
           │ 继承
     ┌─────┴──────┐
     │            │
┌────┴────────┐  ┌┴────────────┐
│CreatorA     │  │CreatorB     │  具体创建者
├─────────────┤  ├─────────────┤
│factoryMethod│  │factoryMethod│ <-- 返回具体产品
└─────────────┘  └─────────────┘

调用流程:
Client → Creator.operation() → factoryMethod() → ConcreteProduct

2.4 与简单工厂的区别

scss 复制代码
简单工厂(不是GoF23种模式之一):
┌─────────────┐      ┌──────────────┐
│   Client    │─────→│SimpleFactory │
└─────────────┘      ├──────────────┤
                     │+ create(type)│
                     └───────┬──────┘
                             │创建
                    ┌────────┼────────┐
                    ▼        ▼        ▼
               ProductA  ProductB  ProductC

工厂方法:
┌─────────────┐      ┌──────────────┐
│   Client    │─────→│   Factory    │
└─────────────┘      ├──────────────┤
                     │+ create()    │抽象方法
                     └───────△──────┘
                             │
                    ┌────────┼────────┐
                    │        │        │
                FactoryA FactoryB FactoryC
                    │        │        │
                创建│    创建│    创建│
                    ▼        ▼        ▼
               ProductA  ProductB  ProductC

三、基础示例

3.1 场景:日志记录器工厂

不同的应用场景需要不同类型的日志记录器(文件日志、数据库日志、远程日志等)。

抽象产品 - 日志记录器:

java 复制代码
/**
 * 抽象产品:日志记录器接口
 */
public interface Logger {
    /**
     * 记录日志
     * @param message 日志消息
     */
    void log(String message);

    /**
     * 获取日志类型
     */
    String getLoggerType();
}

具体产品:

java 复制代码
/**
 * 具体产品:文件日志记录器
 */
public class FileLogger implements Logger {

    private String filePath;

    public FileLogger(String filePath) {
        this.filePath = filePath;
    }

    @Override
    public void log(String message) {
        System.out.println("[FileLogger] 写入文件 " + filePath + ": " + message);
        // 实际实现中会写入文件
    }

    @Override
    public String getLoggerType() {
        return "FILE";
    }
}

/**
 * 具体产品:数据库日志记录器
 */
public class DatabaseLogger implements Logger {

    private String connectionString;

    public DatabaseLogger(String connectionString) {
        this.connectionString = connectionString;
    }

    @Override
    public void log(String message) {
        System.out.println("[DatabaseLogger] 写入数据库 " + connectionString + ": " + message);
        // 实际实现中会写入数据库
    }

    @Override
    public String getLoggerType() {
        return "DATABASE";
    }
}

/**
 * 具体产品:控制台日志记录器
 */
public class ConsoleLogger implements Logger {

    @Override
    public void log(String message) {
        System.out.println("[ConsoleLogger] " + message);
    }

    @Override
    public String getLoggerType() {
        return "CONSOLE";
    }
}

抽象工厂:

java 复制代码
/**
 * 抽象创建者:日志工厂
 */
public abstract class LoggerFactory {

    /**
     * 工厂方法:创建日志记录器(由子类实现)
     */
    protected abstract Logger createLogger();

    /**
     * 业务方法:记录日志
     * 这个方法使用工厂方法创建的产品
     */
    public void writeLog(String message) {
        // 使用工厂方法获取日志记录器
        Logger logger = createLogger();

        // 添加时间戳
        String timestamp = java.time.LocalDateTime.now().toString();
        String formattedMessage = "[" + timestamp + "] " + message;

        // 记录日志
        logger.log(formattedMessage);

        // 可以添加更多通用逻辑
        System.out.println("日志已通过 " + logger.getLoggerType() + " 记录");
    }
}

具体工厂:

java 复制代码
/**
 * 具体创建者:文件日志工厂
 */
public class FileLoggerFactory extends LoggerFactory {

    private String filePath;

    public FileLoggerFactory(String filePath) {
        this.filePath = filePath;
    }

    @Override
    protected Logger createLogger() {
        return new FileLogger(filePath);
    }
}

/**
 * 具体创建者:数据库日志工厂
 */
public class DatabaseLoggerFactory extends LoggerFactory {

    private String connectionString;

    public DatabaseLoggerFactory(String connectionString) {
        this.connectionString = connectionString;
    }

    @Override
    protected Logger createLogger() {
        return new DatabaseLogger(connectionString);
    }
}

/**
 * 具体创建者:控制台日志工厂
 */
public class ConsoleLoggerFactory extends LoggerFactory {

    @Override
    protected Logger createLogger() {
        return new ConsoleLogger();
    }
}

客户端使用:

java 复制代码
public class LoggerTest {
    public static void main(String[] args) {
        // 根据配置选择不同的工厂
        String logType = getLogTypeFromConfig(); // 可以从配置文件读取

        LoggerFactory factory;

        switch (logType) {
            case "file":
                factory = new FileLoggerFactory("/var/log/app.log");
                break;
            case "database":
                factory = new DatabaseLoggerFactory("jdbc:mysql://localhost/logs");
                break;
            default:
                factory = new ConsoleLoggerFactory();
        }

        // 客户端代码不需要知道具体的日志记录器类型
        factory.writeLog("应用程序启动");
        factory.writeLog("用户登录成功");
    }

    private static String getLogTypeFromConfig() {
        // 模拟从配置读取
        return "file";
    }
}

输出:

less 复制代码
[FileLogger] 写入文件 /var/log/app.log: [2025-01-19T10:30:00] 应用程序启动
日志已通过 FILE 记录
[FileLogger] 写入文件 /var/log/app.log: [2025-01-19T10:30:01] 用户登录成功
日志已通过 FILE 记录

四、实际生产场景应用

4.1 场景:数据库连接工厂

在企业应用中,需要支持多种数据库(MySQL、PostgreSQL、Oracle等),每种数据库的连接方式不同。

java 复制代码
/**
 * 抽象产品:数据库连接
 */
public interface DatabaseConnection {
    /**
     * 连接数据库
     */
    void connect();

    /**
     * 执行SQL
     */
    void executeQuery(String sql);

    /**
     * 断开连接
     */
    void disconnect();

    /**
     * 获取数据库类型
     */
    String getDatabaseType();
}

/**
 * 具体产品:MySQL连接
 */
public class MySqlConnection implements DatabaseConnection {

    private String host;
    private int port;
    private String database;
    private boolean connected = false;

    public MySqlConnection(String host, int port, String database) {
        this.host = host;
        this.port = port;
        this.database = database;
    }

    @Override
    public void connect() {
        System.out.println("连接到 MySQL: " + host + ":" + port + "/" + database);
        // 实际实现:使用JDBC连接MySQL
        // Connection conn = DriverManager.getConnection(url, user, password);
        connected = true;
    }

    @Override
    public void executeQuery(String sql) {
        if (!connected) {
            throw new IllegalStateException("未连接到数据库");
        }
        System.out.println("[MySQL] 执行SQL: " + sql);
        // 实际实现:执行SQL查询
    }

    @Override
    public void disconnect() {
        System.out.println("断开 MySQL 连接");
        connected = false;
    }

    @Override
    public String getDatabaseType() {
        return "MySQL";
    }
}

/**
 * 具体产品:PostgreSQL连接
 */
public class PostgreSqlConnection implements DatabaseConnection {

    private String host;
    private int port;
    private String database;
    private boolean connected = false;

    public PostgreSqlConnection(String host, int port, String database) {
        this.host = host;
        this.port = port;
        this.database = database;
    }

    @Override
    public void connect() {
        System.out.println("连接到 PostgreSQL: " + host + ":" + port + "/" + database);
        connected = true;
    }

    @Override
    public void executeQuery(String sql) {
        if (!connected) {
            throw new IllegalStateException("未连接到数据库");
        }
        System.out.println("[PostgreSQL] 执行SQL: " + sql);
    }

    @Override
    public void disconnect() {
        System.out.println("断开 PostgreSQL 连接");
        connected = false;
    }

    @Override
    public String getDatabaseType() {
        return "PostgreSQL";
    }
}

/**
 * 具体产品:Oracle连接
 */
public class OracleConnection implements DatabaseConnection {

    private String host;
    private int port;
    private String sid;
    private boolean connected = false;

    public OracleConnection(String host, int port, String sid) {
        this.host = host;
        this.port = port;
        this.sid = sid;
    }

    @Override
    public void connect() {
        System.out.println("连接到 Oracle: " + host + ":" + port + " SID=" + sid);
        connected = true;
    }

    @Override
    public void executeQuery(String sql) {
        if (!connected) {
            throw new IllegalStateException("未连接到数据库");
        }
        System.out.println("[Oracle] 执行SQL: " + sql);
    }

    @Override
    public void disconnect() {
        System.out.println("断开 Oracle 连接");
        connected = false;
    }

    @Override
    public String getDatabaseType() {
        return "Oracle";
    }
}

抽象工厂和具体工厂:

java 复制代码
/**
 * 抽象创建者:数据库连接工厂
 */
public abstract class DatabaseConnectionFactory {

    /**
     * 工厂方法:创建数据库连接
     */
    protected abstract DatabaseConnection createConnection();

    /**
     * 模板方法:执行数据库操作
     */
    public void executeOperation(String sql) {
        DatabaseConnection connection = null;

        try {
            // 1. 创建连接
            connection = createConnection();

            // 2. 连接数据库
            connection.connect();

            // 3. 执行SQL
            connection.executeQuery(sql);

        } catch (Exception e) {
            System.err.println("数据库操作失败: " + e.getMessage());
        } finally {
            // 4. 关闭连接
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    /**
     * 获取连接池(高级功能)
     */
    public DatabaseConnection getConnection() {
        return createConnection();
    }
}

/**
 * 具体创建者:MySQL连接工厂
 */
public class MySqlConnectionFactory extends DatabaseConnectionFactory {

    private String host;
    private int port;
    private String database;

    public MySqlConnectionFactory(String host, int port, String database) {
        this.host = host;
        this.port = port;
        this.database = database;
    }

    @Override
    protected DatabaseConnection createConnection() {
        return new MySqlConnection(host, port, database);
    }
}

/**
 * 具体创建者:PostgreSQL连接工厂
 */
public class PostgreSqlConnectionFactory extends DatabaseConnectionFactory {

    private String host;
    private int port;
    private String database;

    public PostgreSqlConnectionFactory(String host, int port, String database) {
        this.host = host;
        this.port = port;
        this.database = database;
    }

    @Override
    protected DatabaseConnection createConnection() {
        return new PostgreSqlConnection(host, port, database);
    }
}

/**
 * 具体创建者:Oracle连接工厂
 */
public class OracleConnectionFactory extends DatabaseConnectionFactory {

    private String host;
    private int port;
    private String sid;

    public OracleConnectionFactory(String host, int port, String sid) {
        this.host = host;
        this.port = port;
        this.sid = sid;
    }

    @Override
    protected DatabaseConnection createConnection() {
        return new OracleConnection(host, port, sid);
    }
}

客户端使用:

java 复制代码
public class DatabaseTest {
    public static void main(String[] args) {
        // 从配置文件读取数据库类型
        String dbType = System.getProperty("db.type", "mysql");

        // 根据配置创建对应的工厂
        DatabaseConnectionFactory factory = createFactory(dbType);

        // 执行数据库操作(客户端不需要知道具体数据库类型)
        factory.executeOperation("SELECT * FROM users WHERE id = 1");
        factory.executeOperation("INSERT INTO logs VALUES (1, 'test')");
    }

    private static DatabaseConnectionFactory createFactory(String dbType) {
        switch (dbType.toLowerCase()) {
            case "mysql":
                return new MySqlConnectionFactory("localhost", 3306, "mydb");
            case "postgresql":
                return new PostgreSqlConnectionFactory("localhost", 5432, "mydb");
            case "oracle":
                return new OracleConnectionFactory("localhost", 1521, "ORCL");
            default:
                throw new IllegalArgumentException("不支持的数据库类型: " + dbType);
        }
    }
}

4.2 场景:消息推送工厂

在现代应用中,需要支持多种消息推送渠道(邮件、短信、微信、App推送等)。

java 复制代码
/**
 * 抽象产品:消息推送器
 */
public interface MessagePusher {
    /**
     * 发送消息
     */
    boolean send(String recipient, String title, String content);

    /**
     * 批量发送
     */
    int batchSend(java.util.List<String> recipients, String title, String content);

    /**
     * 获取推送类型
     */
    String getPusherType();
}

/**
 * 具体产品:邮件推送
 */
public class EmailPusher implements MessagePusher {

    private String smtpServer;
    private String username;

    public EmailPusher(String smtpServer, String username) {
        this.smtpServer = smtpServer;
        this.username = username;
    }

    @Override
    public boolean send(String recipient, String title, String content) {
        System.out.println("=== 发送邮件 ===");
        System.out.println("SMTP服务器: " + smtpServer);
        System.out.println("发件人: " + username);
        System.out.println("收件人: " + recipient);
        System.out.println("主题: " + title);
        System.out.println("内容: " + content);
        // 实际实现:使用JavaMail API发送邮件
        return true;
    }

    @Override
    public int batchSend(java.util.List<String> recipients, String title, String content) {
        int successCount = 0;
        for (String recipient : recipients) {
            if (send(recipient, title, content)) {
                successCount++;
            }
        }
        return successCount;
    }

    @Override
    public String getPusherType() {
        return "EMAIL";
    }
}

/**
 * 具体产品:短信推送
 */
public class SmsPusher implements MessagePusher {

    private String apiKey;
    private String gateway;

    public SmsPusher(String apiKey, String gateway) {
        this.apiKey = apiKey;
        this.gateway = gateway;
    }

    @Override
    public boolean send(String recipient, String title, String content) {
        System.out.println("=== 发送短信 ===");
        System.out.println("网关: " + gateway);
        System.out.println("收件人: " + recipient);
        System.out.println("内容: " + content);
        // 实际实现:调用短信网关API
        return true;
    }

    @Override
    public int batchSend(java.util.List<String> recipients, String title, String content) {
        System.out.println("批量发送短信到 " + recipients.size() + " 个号码");
        // 实际实现:调用批量短信API
        return recipients.size();
    }

    @Override
    public String getPusherType() {
        return "SMS";
    }
}

/**
 * 具体产品:微信推送
 */
public class WeChatPusher implements MessagePusher {

    private String appId;
    private String appSecret;

    public WeChatPusher(String appId, String appSecret) {
        this.appId = appId;
        this.appSecret = appSecret;
    }

    @Override
    public boolean send(String recipient, String title, String content) {
        System.out.println("=== 发送微信消息 ===");
        System.out.println("AppID: " + appId);
        System.out.println("OpenID: " + recipient);
        System.out.println("标题: " + title);
        System.out.println("内容: " + content);
        // 实际实现:调用微信模板消息API
        return true;
    }

    @Override
    public int batchSend(java.util.List<String> recipients, String title, String content) {
        System.out.println("批量发送微信消息到 " + recipients.size() + " 个用户");
        return recipients.size();
    }

    @Override
    public String getPusherType() {
        return "WECHAT";
    }
}

/**
 * 抽象工厂:消息推送工厂
 */
public abstract class MessagePusherFactory {

    /**
     * 工厂方法
     */
    protected abstract MessagePusher createPusher();

    /**
     * 发送通知(带重试机制)
     */
    public boolean sendNotification(String recipient, String title, String content) {
        MessagePusher pusher = createPusher();

        // 添加重试逻辑
        int maxRetries = 3;
        for (int i = 0; i < maxRetries; i++) {
            try {
                boolean success = pusher.send(recipient, title, content);
                if (success) {
                    logSuccess(pusher, recipient);
                    return true;
                }
            } catch (Exception e) {
                System.err.println("发送失败,第 " + (i + 1) + " 次重试");
            }
        }

        logFailure(pusher, recipient);
        return false;
    }

    private void logSuccess(MessagePusher pusher, String recipient) {
        System.out.println("[SUCCESS] " + pusher.getPusherType() + " 消息发送成功: " + recipient);
    }

    private void logFailure(MessagePusher pusher, String recipient) {
        System.err.println("[FAILURE] " + pusher.getPusherType() + " 消息发送失败: " + recipient);
    }
}

/**
 * 具体工厂实现
 */
public class EmailPusherFactory extends MessagePusherFactory {
    @Override
    protected MessagePusher createPusher() {
        return new EmailPusher("smtp.example.com", "noreply@example.com");
    }
}

public class SmsPusherFactory extends MessagePusherFactory {
    @Override
    protected MessagePusher createPusher() {
        return new SmsPusher("your-api-key", "https://sms.gateway.com");
    }
}

public class WeChatPusherFactory extends MessagePusherFactory {
    @Override
    protected MessagePusher createPusher() {
        return new WeChatPusher("wx1234567890", "your-app-secret");
    }
}

五、开源框架中的应用

5.1 JDK中的应用

Collection接口的iterator()方法:

java 复制代码
import java.util.*;

/**
 * JDK中最典型的工厂方法模式应用
 * Collection接口定义了iterator()工厂方法
 */
public class JdkIteratorExample {

    public static void main(String[] args) {
        // ArrayList实现了iterator()工厂方法
        Collection<String> list = new ArrayList<>();
        list.add("Java");
        list.add("Python");
        list.add("Go");

        // iterator()是工厂方法,返回Iterator对象
        Iterator<String> iterator = list.iterator();

        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        // HashSet也实现了iterator()工厂方法,返回不同的迭代器实现
        Collection<String> set = new HashSet<>();
        set.add("A");
        set.add("B");

        Iterator<String> setIterator = set.iterator();
        // 使用方式相同,但内部实现不同
    }
}

/**
 * 简化的Collection接口结构
 */
interface SimpleCollection<E> {
    /**
     * 工厂方法:由具体集合类实现
     */
    Iterator<E> iterator();

    boolean add(E e);
    int size();
}

/**
 * 具体实现:ArrayList
 */
class SimpleArrayList<E> implements SimpleCollection<E> {
    private Object[] elements = new Object[10];
    private int size = 0;

    @Override
    public Iterator<E> iterator() {
        // 返回ArrayList特定的迭代器
        return new ArrayListIterator();
    }

    @Override
    public boolean add(E e) {
        elements[size++] = e;
        return true;
    }

    @Override
    public int size() {
        return size;
    }

    /**
     * ArrayList的迭代器实现
     */
    private class ArrayListIterator implements Iterator<E> {
        private int cursor = 0;

        @Override
        public boolean hasNext() {
            return cursor < size;
        }

        @Override
        @SuppressWarnings("unchecked")
        public E next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            return (E) elements[cursor++];
        }
    }
}

/**
 * 具体实现:LinkedList
 */
class SimpleLinkedList<E> implements SimpleCollection<E> {
    private Node<E> first;
    private int size = 0;

    @Override
    public Iterator<E> iterator() {
        // 返回LinkedList特定的迭代器
        return new LinkedListIterator();
    }

    @Override
    public boolean add(E e) {
        Node<E> newNode = new Node<>(e);
        if (first == null) {
            first = newNode;
        } else {
            Node<E> current = first;
            while (current.next != null) {
                current = current.next;
            }
            current.next = newNode;
        }
        size++;
        return true;
    }

    @Override
    public int size() {
        return size;
    }

    /**
     * LinkedList的迭代器实现
     */
    private class LinkedListIterator implements Iterator<E> {
        private Node<E> current = first;

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public E next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            E data = current.data;
            current = current.next;
            return data;
        }
    }

    private static class Node<E> {
        E data;
        Node<E> next;

        Node(E data) {
            this.data = data;
        }
    }
}

5.2 JDBC中的DriverManager

java 复制代码
import java.sql.*;

/**
 * JDBC的DriverManager是工厂方法模式的经典应用
 * getConnection()方法根据URL选择合适的Driver创建Connection
 */
public class JdbcFactoryExample {

    public static void demonstrateJdbcFactory() throws SQLException {
        // DriverManager.getConnection()是静态工厂方法
        // 根据URL前缀选择对应的Driver

        // MySQL Driver
        Connection mysqlConn = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/mydb",
            "user",
            "password"
        );

        // PostgreSQL Driver
        Connection pgConn = DriverManager.getConnection(
            "jdbc:postgresql://localhost:5432/mydb",
            "user",
            "password"
        );

        // 客户端代码使用相同的Connection接口
        Statement stmt = mysqlConn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT * FROM users");
    }
}

/**
 * 简化的JDBC工厂方法模式实现
 */
interface Driver {
    Connection connect(String url, java.util.Properties info) throws SQLException;
    boolean acceptsURL(String url) throws SQLException;
}

class DriverManager {
    private static java.util.List<Driver> registeredDrivers = new java.util.ArrayList<>();

    /**
     * 工厂方法:根据URL创建合适的Connection
     */
    public static Connection getConnection(String url, String user, String password)
            throws SQLException {

        // 遍历注册的Driver,找到能处理该URL的Driver
        for (Driver driver : registeredDrivers) {
            if (driver.acceptsURL(url)) {
                java.util.Properties info = new java.util.Properties();
                info.put("user", user);
                info.put("password", password);

                // 使用找到的Driver创建Connection
                Connection conn = driver.connect(url, info);
                if (conn != null) {
                    return conn;
                }
            }
        }

        throw new SQLException("No suitable driver found for " + url);
    }

    public static void registerDriver(Driver driver) {
        registeredDrivers.add(driver);
    }
}

5.3 Spring Framework中的FactoryBean

java 复制代码
import org.springframework.beans.factory.FactoryBean;

/**
 * Spring的FactoryBean是工厂方法模式的应用
 * 用于创建复杂对象或需要特殊初始化的对象
 */
public class ConnectionPoolFactoryBean implements FactoryBean<ConnectionPool> {

    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private int maxConnections = 10;

    /**
     * 工厂方法:创建ConnectionPool对象
     */
    @Override
    public ConnectionPool getObject() throws Exception {
        ConnectionPool pool = new ConnectionPool();
        pool.setDriverClassName(driverClassName);
        pool.setUrl(url);
        pool.setUsername(username);
        pool.setPassword(password);
        pool.setMaxConnections(maxConnections);

        // 初始化连接池
        pool.initialize();

        return pool;
    }

    @Override
    public Class<?> getObjectType() {
        return ConnectionPool.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    // Setters for dependency injection
    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }
}

/**
 * 连接池类
 */
class ConnectionPool {
    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private int maxConnections;

    public void initialize() {
        System.out.println("初始化连接池: " + url);
        // 创建初始连接
    }

    // Getters and Setters
    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }
}

/**
 * Spring配置文件使用示例:
 *
 * <bean id="connectionPool" class="com.example.ConnectionPoolFactoryBean">
 *     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
 *     <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
 *     <property name="username" value="root"/>
 *     <property name="password" value="password"/>
 *     <property name="maxConnections" value="20"/>
 * </bean>
 *
 * 当获取connectionPool bean时,实际得到的是FactoryBean.getObject()返回的对象
 */

5.4 Guava中的LoadingCache

java 复制代码
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

/**
 * Guava的CacheBuilder使用工厂方法模式
 * CacheLoader.load()是工厂方法,用于创建缓存值
 */
public class GuavaCacheFactoryExample {

    public static void main(String[] args) throws Exception {
        // CacheLoader定义了如何创建缓存值
        LoadingCache<String, User> userCache = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .build(new CacheLoader<String, User>() {
                @Override
                public User load(String userId) throws Exception {
                    // 工厂方法:当缓存中没有该key时,调用此方法创建对象
                    return loadUserFromDatabase(userId);
                }
            });

        // 使用缓存
        User user1 = userCache.get("user123"); // 第一次访问,调用load()
        User user2 = userCache.get("user123"); // 第二次访问,直接从缓存获取

        System.out.println("两次获取的是同一个对象: " + (user1 == user2));
    }

    private static User loadUserFromDatabase(String userId) {
        System.out.println("从数据库加载用户: " + userId);
        User user = new User();
        user.setName("User-" + userId);
        user.setEmail(userId + "@example.com");
        return user;
    }
}

/**
 * 自定义实现类似的缓存工厂
 */
abstract class DataLoader<K, V> {
    private java.util.Map<K, V> cache = new java.util.HashMap<>();

    /**
     * 工厂方法:由子类实现具体的加载逻辑
     */
    protected abstract V load(K key) throws Exception;

    /**
     * 获取数据(带缓存)
     */
    public V get(K key) throws Exception {
        if (cache.containsKey(key)) {
            return cache.get(key);
        }

        // 调用工厂方法创建对象
        V value = load(key);
        cache.put(key, value);
        return value;
    }
}

/**
 * 具体实现:用户数据加载器
 */
class UserDataLoader extends DataLoader<String, User> {
    @Override
    protected User load(String userId) throws Exception {
        System.out.println("从数据源加载用户: " + userId);
        // 实际实现:从数据库、API等加载数据
        User user = new User();
        user.setName("User-" + userId);
        return user;
    }
}

六、工厂方法模式的优缺点

6.1 优点

1. 符合开闭原则

arduino 复制代码
添加新产品时的变化:

不使用工厂方法:
❌ 修改客户端代码中的switch/if语句
❌ 直接在代码中new具体类

使用工厂方法:
✓ 创建新的产品类
✓ 创建新的工厂类
✓ 客户端代码无需修改

2. 符合单一职责原则

  • 产品创建逻辑集中在工厂类中
  • 客户端只关注产品的使用,不关心创建细节

3. 符合依赖倒置原则

arduino 复制代码
依赖关系:

Client → AbstractFactory → AbstractProduct
              ↑                  ↑
              │                  │
      ConcreteFactory    ConcreteProduct

客户端依赖抽象,不依赖具体实现

4. 提高代码复用性

  • 通用的创建逻辑可以在抽象工厂中实现
  • 子类只需实现特定的创建细节

6.2 缺点

1. 类的数量增加

diff 复制代码
每增加一个产品:
- 1个具体产品类
- 1个具体工厂类

产品种类多时,类的数量会快速增长

2. 增加系统复杂度

  • 引入了额外的抽象层
  • 需要理解工厂和产品的关系

3. 增加理解难度

  • 对象创建逻辑分散在多个类中
  • 新手可能难以快速定位代码

七、最佳实践

7.1 使用配置文件驱动工厂选择

java 复制代码
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 使用配置文件选择工厂
 */
public class ConfigDrivenFactory {

    private static Properties config = new Properties();

    static {
        try (InputStream is = ConfigDrivenFactory.class
                .getResourceAsStream("/factory.properties")) {
            config.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据配置创建工厂
     */
    public static LoggerFactory createLoggerFactory() {
        String logType = config.getProperty("logger.type", "console");

        switch (logType) {
            case "file":
                String filePath = config.getProperty("logger.file.path", "/tmp/app.log");
                return new FileLoggerFactory(filePath);
            case "database":
                String connStr = config.getProperty("logger.db.connection");
                return new DatabaseLoggerFactory(connStr);
            default:
                return new ConsoleLoggerFactory();
        }
    }
}

/**
 * factory.properties配置文件内容:
 *
 * logger.type=file
 * logger.file.path=/var/log/application.log
 *
 * # 或者
 * logger.type=database
 * logger.db.connection=jdbc:mysql://localhost/logs
 */

7.2 结合反射实现通用工厂

java 复制代码
/**
 * 使用反射的通用工厂
 */
public class ReflectionFactory<T> {

    private Class<? extends T> productClass;

    public ReflectionFactory(Class<? extends T> productClass) {
        this.productClass = productClass;
    }

    /**
     * 工厂方法:使用反射创建对象
     */
    public T createProduct() {
        try {
            return productClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("无法创建对象: " + productClass.getName(), e);
        }
    }

    /**
     * 带参数的工厂方法
     */
    public T createProduct(Object... args) {
        try {
            // 获取参数类型
            Class<?>[] paramTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                paramTypes[i] = args[i].getClass();
            }

            // 获取构造函数并创建对象
            return productClass.getDeclaredConstructor(paramTypes).newInstance(args);
        } catch (Exception e) {
            throw new RuntimeException("无法创建对象: " + productClass.getName(), e);
        }
    }
}

/**
 * 使用示例
 */
class ReflectionFactoryTest {
    public static void main(String[] args) {
        // 创建ConsoleLogger
        ReflectionFactory<Logger> factory1 =
            new ReflectionFactory<>(ConsoleLogger.class);
        Logger logger1 = factory1.createProduct();
        logger1.log("测试消息");

        // 创建FileLogger(带参数)
        ReflectionFactory<Logger> factory2 =
            new ReflectionFactory<>(FileLogger.class);
        Logger logger2 = factory2.createProduct("/tmp/test.log");
        logger2.log("测试消息");
    }
}

7.3 使用注解标记工厂

java 复制代码
import java.lang.annotation.*;

/**
 * 工厂注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FactoryProduct {
    String type();
}

/**
 * 使用注解标记产品类型
 */
@FactoryProduct(type = "console")
class ConsoleLoggerV2 implements Logger {
    @Override
    public void log(String message) {
        System.out.println(message);
    }

    @Override
    public String getLoggerType() {
        return "CONSOLE";
    }
}

@FactoryProduct(type = "file")
class FileLoggerV2 implements Logger {
    @Override
    public void log(String message) {
        System.out.println("[File] " + message);
    }

    @Override
    public String getLoggerType() {
        return "FILE";
    }
}

/**
 * 基于注解的工厂
 */
class AnnotationBasedFactory {

    private static java.util.Map<String, Class<? extends Logger>> registry =
        new java.util.HashMap<>();

    /**
     * 注册所有带@FactoryProduct注解的类
     */
    public static void scanAndRegister(String packageName) {
        // 简化实现,实际需要使用classpath扫描
        registerProduct(ConsoleLoggerV2.class);
        registerProduct(FileLoggerV2.class);
    }

    private static void registerProduct(Class<? extends Logger> clazz) {
        FactoryProduct annotation = clazz.getAnnotation(FactoryProduct.class);
        if (annotation != null) {
            registry.put(annotation.type(), clazz);
        }
    }

    /**
     * 根据类型创建产品
     */
    public static Logger createLogger(String type) {
        Class<? extends Logger> clazz = registry.get(type);
        if (clazz == null) {
            throw new IllegalArgumentException("未找到类型: " + type);
        }

        try {
            return clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("创建失败", e);
        }
    }
}

7.4 工厂方法+单例模式

java 复制代码
/**
 * 工厂方法与单例模式结合
 * 确保每种类型的产品只创建一次
 */
public abstract class SingletonFactory<T> {

    private T instance;

    /**
     * 抽象工厂方法
     */
    protected abstract T createInstance();

    /**
     * 获取单例实例
     */
    public synchronized T getInstance() {
        if (instance == null) {
            instance = createInstance();
        }
        return instance;
    }
}

/**
 * 具体实现
 */
class DatabaseConnectionSingletonFactory extends SingletonFactory<DatabaseConnection> {

    @Override
    protected DatabaseConnection createInstance() {
        System.out.println("创建数据库连接(仅一次)");
        return new MySqlConnection("localhost", 3306, "mydb");
    }
}

/**
 * 使用示例
 */
class SingletonFactoryTest {
    public static void main(String[] args) {
        SingletonFactory<DatabaseConnection> factory =
            new DatabaseConnectionSingletonFactory();

        DatabaseConnection conn1 = factory.getInstance();
        DatabaseConnection conn2 = factory.getInstance();

        System.out.println("是同一个对象: " + (conn1 == conn2)); // true
    }
}

八、工厂方法 vs 其他创建型模式

8.1 工厂方法 vs 抽象工厂

diff 复制代码
工厂方法(Factory Method):
┌─────────────┐
│   Factory   │
├─────────────┤
│+ create()   │ ──→ 创建一个产品
└─────────────┘

抽象工厂(Abstract Factory):
┌─────────────────┐
│ AbstractFactory │
├─────────────────┤
│+ createProductA │ ──→ 创建产品A
│+ createProductB │ ──→ 创建产品B
│+ createProductC │ ──→ 创建产品C
└─────────────────┘

区别:
- 工厂方法:一个工厂创建一种产品
- 抽象工厂:一个工厂创建一系列相关产品

8.2 工厂方法 vs 建造者模式

scss 复制代码
工厂方法:
- 关注点:创建什么产品
- 复杂度:产品创建相对简单
- 使用场景:产品类型多样

建造者模式:
- 关注点:如何创建产品
- 复杂度:产品创建过程复杂
- 使用场景:产品内部结构复杂

示例对比:
// 工厂方法
Logger logger = factory.createLogger();

// 建造者
User user = new UserBuilder()
    .setName("John")
    .setAge(30)
    .setEmail("john@example.com")
    .build();

九、实战演练:构建文档转换器工厂

java 复制代码
/**
 * 抽象产品:文档转换器
 */
public interface DocumentConverter {
    /**
     * 转换文档
     * @param sourceFile 源文件路径
     * @param targetFile 目标文件路径
     * @return 是否转换成功
     */
    boolean convert(String sourceFile, String targetFile);

    /**
     * 获取支持的源格式
     */
    String getSourceFormat();

    /**
     * 获取支持的目标格式
     */
    String getTargetFormat();
}

/**
 * 具体产品:PDF转Word
 */
public class PdfToWordConverter implements DocumentConverter {

    @Override
    public boolean convert(String sourceFile, String targetFile) {
        System.out.println("开始转换: " + sourceFile + " -> " + targetFile);
        System.out.println("使用Apache PDFBox解析PDF...");
        System.out.println("使用Apache POI生成Word文档...");
        System.out.println("转换完成!");
        return true;
    }

    @Override
    public String getSourceFormat() {
        return "PDF";
    }

    @Override
    public String getTargetFormat() {
        return "WORD";
    }
}

/**
 * 具体产品:Word转PDF
 */
public class WordToPdfConverter implements DocumentConverter {

    @Override
    public boolean convert(String sourceFile, String targetFile) {
        System.out.println("开始转换: " + sourceFile + " -> " + targetFile);
        System.out.println("使用Apache POI解析Word文档...");
        System.out.println("使用iText生成PDF...");
        System.out.println("转换完成!");
        return true;
    }

    @Override
    public String getSourceFormat() {
        return "WORD";
    }

    @Override
    public String getTargetFormat() {
        return "PDF";
    }
}

/**
 * 具体产品:Markdown转HTML
 */
public class MarkdownToHtmlConverter implements DocumentConverter {

    @Override
    public boolean convert(String sourceFile, String targetFile) {
        System.out.println("开始转换: " + sourceFile + " -> " + targetFile);
        System.out.println("使用CommonMark解析Markdown...");
        System.out.println("生成HTML文件...");
        System.out.println("转换完成!");
        return true;
    }

    @Override
    public String getSourceFormat() {
        return "MARKDOWN";
    }

    @Override
    public String getTargetFormat() {
        return "HTML";
    }
}

/**
 * 抽象工厂:文档转换器工厂
 */
public abstract class DocumentConverterFactory {

    /**
     * 工厂方法:创建转换器
     */
    protected abstract DocumentConverter createConverter();

    /**
     * 执行转换(模板方法)
     */
    public boolean performConversion(String sourceFile, String targetFile) {
        try {
            // 1. 验证文件
            validateFiles(sourceFile, targetFile);

            // 2. 创建转换器
            DocumentConverter converter = createConverter();

            // 3. 显示转换信息
            System.out.println("\n=== 文档转换任务 ===");
            System.out.println("转换类型: " + converter.getSourceFormat() +
                             " → " + converter.getTargetFormat());
            System.out.println("源文件: " + sourceFile);
            System.out.println("目标文件: " + targetFile);
            System.out.println("==================\n");

            // 4. 执行转换
            boolean success = converter.convert(sourceFile, targetFile);

            // 5. 记录日志
            if (success) {
                logSuccess(converter, sourceFile, targetFile);
            } else {
                logFailure(converter, sourceFile, targetFile);
            }

            return success;

        } catch (Exception e) {
            System.err.println("转换失败: " + e.getMessage());
            return false;
        }
    }

    private void validateFiles(String sourceFile, String targetFile) {
        if (sourceFile == null || targetFile == null) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
    }

    private void logSuccess(DocumentConverter converter, String source, String target) {
        System.out.println("\n✓ 转换成功: " + converter.getSourceFormat() +
                         " → " + converter.getTargetFormat());
    }

    private void logFailure(DocumentConverter converter, String source, String target) {
        System.err.println("\n✗ 转换失败: " + converter.getSourceFormat() +
                         " → " + converter.getTargetFormat());
    }
}

/**
 * 具体工厂实现
 */
public class PdfToWordConverterFactory extends DocumentConverterFactory {
    @Override
    protected DocumentConverter createConverter() {
        return new PdfToWordConverter();
    }
}

public class WordToPdfConverterFactory extends DocumentConverterFactory {
    @Override
    protected DocumentConverter createConverter() {
        return new WordToPdfConverter();
    }
}

public class MarkdownToHtmlConverterFactory extends DocumentConverterFactory {
    @Override
    protected DocumentConverter createConverter() {
        return new MarkdownToHtmlConverter();
    }
}

/**
 * 工厂注册表(简化客户端使用)
 */
public class ConverterFactoryRegistry {

    private static java.util.Map<String, DocumentConverterFactory> factories =
        new java.util.HashMap<>();

    static {
        // 注册所有工厂
        factories.put("pdf-to-word", new PdfToWordConverterFactory());
        factories.put("word-to-pdf", new WordToPdfConverterFactory());
        factories.put("markdown-to-html", new MarkdownToHtmlConverterFactory());
    }

    /**
     * 根据转换类型获取工厂
     */
    public static DocumentConverterFactory getFactory(String conversionType) {
        DocumentConverterFactory factory = factories.get(conversionType);
        if (factory == null) {
            throw new IllegalArgumentException("不支持的转换类型: " + conversionType);
        }
        return factory;
    }

    /**
     * 列出所有支持的转换类型
     */
    public static void listSupportedConversions() {
        System.out.println("支持的转换类型:");
        factories.keySet().forEach(type -> System.out.println("  - " + type));
    }
}

/**
 * 测试类
 */
public class DocumentConverterTest {
    public static void main(String[] args) {
        // 列出支持的转换类型
        ConverterFactoryRegistry.listSupportedConversions();

        // 场景1: PDF转Word
        DocumentConverterFactory factory1 =
            ConverterFactoryRegistry.getFactory("pdf-to-word");
        factory1.performConversion("/docs/report.pdf", "/docs/report.docx");

        // 场景2: Word转PDF
        DocumentConverterFactory factory2 =
            ConverterFactoryRegistry.getFactory("word-to-pdf");
        factory2.performConversion("/docs/article.docx", "/docs/article.pdf");

        // 场景3: Markdown转HTML
        DocumentConverterFactory factory3 =
            ConverterFactoryRegistry.getFactory("markdown-to-html");
        factory3.performConversion("/docs/README.md", "/docs/README.html");
    }
}

十、总结

10.1 核心要点

  1. 工厂方法模式的本质:定义创建对象的接口,由子类决定实例化哪个类
  2. 适用场景
    • 不知道具体需要哪个类的实例
    • 希望通过子类指定创建对象
    • 将对象创建职责委托给子类
  3. 关键技巧
    • 抽象工厂定义工厂方法
    • 具体工厂实现工厂方法
    • 客户端依赖抽象,不依赖具体

10.2 使用建议

arduino 复制代码
选择工厂方法模式的检查清单:

✓ 是否需要在运行时决定创建哪个类的实例?
✓ 是否有多种产品类型,且可能继续增加?
✓ 是否希望将对象创建逻辑与使用逻辑分离?
✓ 是否需要在不修改客户端代码的情况下扩展新产品?

如果以上都是"是",那么工厂方法模式是个好选择!

10.3 实践原则

  1. 合理使用:不是所有对象创建都需要工厂方法
  2. 保持简单:如果只有2-3个产品,考虑简单工厂
  3. 结合其他模式:可以与单例、原型等模式结合
  4. 文档化:清晰记录各工厂创建的产品类型

相关推荐
杨充9 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充9 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充9 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充10 小时前
2.面向对象的特性
设计模式
杨充10 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充10 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充10 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯13 小时前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯21 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式