深入理解设计模式之工厂方法模式(Factory Method Pattern)
一、引言
在软件开发中,对象创建是一个非常常见的操作。但是,如果在代码中直接使用new操作符创建对象,会导致代码与具体类紧密耦合,降低系统的灵活性和可扩展性。工厂方法模式正是为了解决这个问题而诞生的一种创建型设计模式。
本文将深入探讨工厂方法模式的原理、实现方式,并结合实际生产场景和开源框架(如JDK、Spring、Guava)中的应用,帮助你全面掌握这一重要的设计模式。
二、什么是工厂方法模式
2.1 定义
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。
2.2 核心思想
- 封装对象创建:将对象的创建逻辑封装在工厂方法中
- 依赖抽象而非具体:客户端依赖抽象产品和抽象工厂
- 开闭原则:添加新产品时无需修改现有代码
- 单一职责:将产品创建职责分离到专门的工厂类
2.3 模式结构
┌──────────────────────┐
│ <<interface>> │
│ Product │ 抽象产品
├──────────────────────┤
│ + operation() │
└──────────────────────┘
△
│ 实现
┌─────┴──────┐
│ │
┌────┴─────┐ ┌──┴────────┐
│ConcreteA │ │ConcreteB │ 具体产品
└──────────┘ └───────────┘
△ △
│创建 │创建
│ │
┌────┴─────────────┴────┐
│ <<abstract>> │
│ Creator │ 抽象创建者
├────────────────────────┤
│ + factoryMethod() │ <-- 工厂方法(抽象)
│ + operation() │ <-- 使用产品的业务方法
└────────────────────────┘
△
│ 继承
┌─────┴──────┐
│ │
┌────┴────────┐ ┌┴────────────┐
│CreatorA │ │CreatorB │ 具体创建者
├─────────────┤ ├─────────────┤
│factoryMethod│ │factoryMethod│ <-- 返回具体产品
└─────────────┘ └─────────────┘
调用流程:
Client → Creator.operation() → factoryMethod() → ConcreteProduct
2.4 与简单工厂的区别
简单工厂(不是GoF23种模式之一):
┌─────────────┐ ┌──────────────┐
│ Client │─────→│SimpleFactory │
└─────────────┘ ├──────────────┤
│+ create(type)│
└───────┬──────┘
│创建
┌────────┼────────┐
▼ ▼ ▼
ProductA ProductB ProductC
工厂方法:
┌─────────────┐ ┌──────────────┐
│ Client │─────→│ Factory │
└─────────────┘ ├──────────────┤
│+ create() │抽象方法
└───────△──────┘
│
┌────────┼────────┐
│ │ │
FactoryA FactoryB FactoryC
│ │ │
创建│ 创建│ 创建│
▼ ▼ ▼
ProductA ProductB ProductC
三、基础示例
3.1 场景:日志记录器工厂
不同的应用场景需要不同类型的日志记录器(文件日志、数据库日志、远程日志等)。
抽象产品 - 日志记录器:
/**
* 抽象产品:日志记录器接口
*/
public interface Logger {
/**
* 记录日志
* @param message 日志消息
*/
void log(String message);
/**
* 获取日志类型
*/
String getLoggerType();
}
具体产品:
/**
* 具体产品:文件日志记录器
*/
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";
}
}
抽象工厂:
/**
* 抽象创建者:日志工厂
*/
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() + " 记录");
}
}
具体工厂:
/**
* 具体创建者:文件日志工厂
*/
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();
}
}
客户端使用:
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";
}
}
输出:
[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等),每种数据库的连接方式不同。
/**
* 抽象产品:数据库连接
*/
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";
}
}
抽象工厂和具体工厂:
/**
* 抽象创建者:数据库连接工厂
*/
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);
}
}
客户端使用:
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推送等)。
/**
* 抽象产品:消息推送器
*/
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()方法:
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
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
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
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. 符合开闭原则
添加新产品时的变化:
不使用工厂方法:
❌ 修改客户端代码中的switch/if语句
❌ 直接在代码中new具体类
使用工厂方法:
✓ 创建新的产品类
✓ 创建新的工厂类
✓ 客户端代码无需修改
2. 符合单一职责原则
- 产品创建逻辑集中在工厂类中
- 客户端只关注产品的使用,不关心创建细节
3. 符合依赖倒置原则
依赖关系:
Client → AbstractFactory → AbstractProduct
↑ ↑
│ │
ConcreteFactory ConcreteProduct
客户端依赖抽象,不依赖具体实现
4. 提高代码复用性
- 通用的创建逻辑可以在抽象工厂中实现
- 子类只需实现特定的创建细节
6.2 缺点
1. 类的数量增加
每增加一个产品:
- 1个具体产品类
- 1个具体工厂类
产品种类多时,类的数量会快速增长
2. 增加系统复杂度
- 引入了额外的抽象层
- 需要理解工厂和产品的关系
3. 增加理解难度
- 对象创建逻辑分散在多个类中
- 新手可能难以快速定位代码
七、最佳实践
7.1 使用配置文件驱动工厂选择
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 结合反射实现通用工厂
/**
* 使用反射的通用工厂
*/
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 使用注解标记工厂
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 工厂方法+单例模式
/**
* 工厂方法与单例模式结合
* 确保每种类型的产品只创建一次
*/
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 抽象工厂
工厂方法(Factory Method):
┌─────────────┐
│ Factory │
├─────────────┤
│+ create() │ ──→ 创建一个产品
└─────────────┘
抽象工厂(Abstract Factory):
┌─────────────────┐
│ AbstractFactory │
├─────────────────┤
│+ createProductA │ ──→ 创建产品A
│+ createProductB │ ──→ 创建产品B
│+ createProductC │ ──→ 创建产品C
└─────────────────┘
区别:
- 工厂方法:一个工厂创建一种产品
- 抽象工厂:一个工厂创建一系列相关产品
8.2 工厂方法 vs 建造者模式
工厂方法:
- 关注点:创建什么产品
- 复杂度:产品创建相对简单
- 使用场景:产品类型多样
建造者模式:
- 关注点:如何创建产品
- 复杂度:产品创建过程复杂
- 使用场景:产品内部结构复杂
示例对比:
// 工厂方法
Logger logger = factory.createLogger();
// 建造者
User user = new UserBuilder()
.setName("John")
.setAge(30)
.setEmail("john@example.com")
.build();
九、实战演练:构建文档转换器工厂
/**
* 抽象产品:文档转换器
*/
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 核心要点
- 工厂方法模式的本质:定义创建对象的接口,由子类决定实例化哪个类
- 适用场景:
- 不知道具体需要哪个类的实例
- 希望通过子类指定创建对象
- 将对象创建职责委托给子类
- 关键技巧:
- 抽象工厂定义工厂方法
- 具体工厂实现工厂方法
- 客户端依赖抽象,不依赖具体
10.2 使用建议
选择工厂方法模式的检查清单:
✓ 是否需要在运行时决定创建哪个类的实例?
✓ 是否有多种产品类型,且可能继续增加?
✓ 是否希望将对象创建逻辑与使用逻辑分离?
✓ 是否需要在不修改客户端代码的情况下扩展新产品?
如果以上都是"是",那么工厂方法模式是个好选择!
10.3 实践原则
- 合理使用:不是所有对象创建都需要工厂方法
- 保持简单:如果只有2-3个产品,考虑简单工厂
- 结合其他模式:可以与单例、原型等模式结合
- 文档化:清晰记录各工厂创建的产品类型