工厂模式(Factory)
工厂模式是最常用的创建型设计模式之一。它提供了一种创建对象的最佳方式,将对象的创建与使用分离,降低了系统的耦合度。
模式概述
工厂模式分为三种:
- 简单工厂模式(Simple Factory):不属于 GoF 23 种模式,但很常用
- 工厂方法模式(Factory Method):定义创建对象的接口,让子类决定实例化哪个类
- 抽象工厂模式(Abstract Factory):创建相关或依赖对象的家族
简单工厂模式
问题场景
假设我们需要根据不同的类型创建不同的日志记录器:
// 问题代码:客户端需要知道具体类
public class Client {
public void log(String type, String message) {
if ("file".equals(type)) {
FileLogger logger = new FileLogger();
logger.log(message);
} else if ("database".equals(type)) {
DatabaseLogger logger = new DatabaseLogger();
logger.log(message);
} else if ("console".equals(type)) {
ConsoleLogger logger = new ConsoleLogger();
logger.log(message);
}
// 新增类型需要修改这里
}
}
存在的问题:
- 客户端需要知道所有具体类
- 创建逻辑分散在客户端代码中
- 新增产品类型需要修改客户端代码
解决方案
将创建逻辑封装到一个工厂类中:
// 产品接口
public interface Logger {
void log(String message);
}
// 具体产品
public class FileLogger implements Logger {
public void log(String message) {
System.out.println("写入文件日志: " + message);
}
}
public class DatabaseLogger implements Logger {
public void log(String message) {
System.out.println("写入数据库日志: " + message);
}
}
public class ConsoleLogger implements Logger {
public void log(String message) {
System.out.println("控制台日志: " + message);
}
}
// 简单工厂
public class LoggerFactory {
public static Logger createLogger(String type) {
switch (type) {
case "file":
return new FileLogger();
case "database":
return new DatabaseLogger();
case "console":
return new ConsoleLogger();
default:
throw new IllegalArgumentException("未知的日志类型: " + type);
}
}
}
// 客户端使用
public class Client {
public void log(String type, String message) {
Logger logger = LoggerFactory.createLogger(type);
logger.log(message);
}
}
模式结构
优缺点
优点:
- 将对象创建和使用分离
- 客户端不需要知道具体类名
- 集中管理创建逻辑
缺点:
- 违反开闭原则(新增产品需要修改工厂类)
- 工厂类职责过重
工厂方法模式
模式定义
工厂方法模式:定义一个创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。
问题场景
继续日志记录器的例子,如果要新增日志类型,简单工厂需要修改工厂类。使用工厂方法模式可以解决这个问题:
解决方案
// 产品接口
public interface Logger {
void log(String message);
}
// 具体产品
public class FileLogger implements Logger {
public void log(String message) {
System.out.println("写入文件日志: " + message);
}
}
public class DatabaseLogger implements Logger {
public void log(String message) {
System.out.println("写入数据库日志: " + message);
}
}
// 工厂接口
public interface LoggerFactory {
Logger createLogger();
}
// 具体工厂
public class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() {
// 可以在这里进行复杂的初始化
return new FileLogger();
}
}
public class DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
return new DatabaseLogger();
}
}
// 客户端使用
public class Client {
private LoggerFactory factory;
public Client(LoggerFactory factory) {
this.factory = factory;
}
public void log(String message) {
Logger logger = factory.createLogger();
logger.log(message);
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
// 使用文件日志
Client client1 = new Client(new FileLoggerFactory());
client1.log("文件日志测试");
// 使用数据库日志
Client client2 = new Client(new DatabaseLoggerFactory());
client2.log("数据库日志测试");
}
}
模式结构
优缺点
优点:
- 符合开闭原则:新增产品只需新增工厂类
- 符合单一职责:每个工厂只负责创建一种产品
- 解耦:客户端不需要知道具体产品类
缺点:
- 类的数量增加
- 增加系统抽象性
抽象工厂模式
模式定义
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
问题场景
假设我们需要开发一个跨平台的 UI 组件库,需要支持 Windows 和 Mac 两种风格:
// 问题代码:客户端需要处理不同平台
public class Application {
public void createUI(String platform) {
if ("windows".equals(platform)) {
WindowsButton button = new WindowsButton();
WindowsCheckbox checkbox = new WindowsCheckbox();
button.render();
checkbox.render();
} else if ("mac".equals(platform)) {
MacButton button = new MacButton();
MacCheckbox checkbox = new MacCheckbox();
button.render();
checkbox.render();
}
}
}
解决方案
// 抽象产品 - 按钮
public interface Button {
void render();
}
// 抽象产品 - 复选框
public interface Checkbox {
void render();
}
// 具体产品 - Windows 风格
public class WindowsButton implements Button {
public void render() {
System.out.println("渲染 Windows 风格按钮");
}
}
public class WindowsCheckbox implements Checkbox {
public void render() {
System.out.println("渲染 Windows 风格复选框");
}
}
// 具体产品 - Mac 风格
public class MacButton implements Button {
public void render() {
System.out.println("渲染 Mac 风格按钮");
}
}
public class MacCheckbox implements Checkbox {
public void render() {
System.out.println("渲染 Mac 风格复选框");
}
}
// 抽象工厂
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// 具体工厂 - Windows
public class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
// 具体工厂 - Mac
public class MacFactory implements GUIFactory {
public Button createButton() {
return new MacButton();
}
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}
// 客户端
public class Application {
private Button button;
private Checkbox checkbox;
public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void render() {
button.render();
checkbox.render();
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
// 根据配置选择工厂
GUIFactory factory;
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
factory = new WindowsFactory();
} else {
factory = new MacFactory();
}
Application app = new Application(factory);
app.render();
}
}
模式结构
优缺点
优点:
- 保证同一产品族的产品一起使用
- 客户端不需要知道具体产品类
- 符合开闭原则
缺点:
- 新增产品类型需要修改所有工厂类
- 增加系统抽象性和复杂度
三种工厂模式对比
| 特性 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 产品数量 | 多种 | 一种 | 多种(产品族) |
| 工厂数量 | 一个 | 多个 | 多个 |
| 开闭原则 | 违反 | 符合 | 部分符合 |
| 扩展产品 | 修改工厂 | 新增工厂 | 修改所有工厂 |
| 扩展产品族 | 容易 | 不适用 | 容易 |
| 复杂度 | 低 | 中 | 高 |
实际应用案例
1. JDBC 驱动管理
// DriverManager 是简单工厂
Connection conn = DriverManager.getConnection(url, username, password);
// 根据不同的 URL 返回不同数据库的连接
2. Spring BeanFactory
// Spring 的 BeanFactory 是工厂模式的典型应用
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
3. Java 集合框架
// List、Set、Map 都是抽象工厂
List<String> list = new ArrayList<>(); // 具体工厂 ArrayList
List<String> list2 = new LinkedList<>(); // 具体工厂 LinkedList
最佳实践
使用场景
简单工厂:
- 产品种类较少
- 客户端不需要知道具体产品类
- 不需要频繁扩展
工厂方法:
- 需要扩展产品种类
- 需要将创建逻辑延迟到子类
- 需要解耦客户端和具体产品
抽象工厂:
- 需要创建产品族
- 需要保证产品族的一致性
- 系统需要独立于产品的创建、组合和表示
实现建议
- 优先使用工厂方法:比简单工厂更灵活
- 结合配置文件:可以通过配置选择工厂
- 利用反射:可以进一步解耦
// 使用反射的工厂
public class ReflectiveFactory {
public static <T> T create(Class<T> clazz) {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("创建实例失败", e);
}
}
}
小结
工厂模式是创建型模式的核心:
- 简单工厂:封装创建逻辑,适合产品种类少的场景
- 工厂方法:延迟到子类创建,符合开闭原则
- 抽象工厂:创建产品族,保证产品一致性
选择哪种工厂模式取决于具体需求:
- 产品种类少 → 简单工厂
- 需要扩展 → 工厂方法
- 需要产品族 → 抽象工厂
练习
- 实现一个简单工厂,创建不同形状的对象
- 使用工厂方法模式实现一个文档解析器(支持 JSON、XML、YAML)
- 使用抽象工厂模式实现一个数据库访问层(支持 MySQL、PostgreSQL)
- 比较三种工厂模式的优劣
参考资源
- 《设计模式:可复用面向对象软件的基础》
- 《Head First 设计模式》
- Oracle Java Documentation