跳到主要内容

工厂模式(Factory)

工厂模式是最常用的创建型设计模式之一。它提供了一种创建对象的最佳方式,将对象的创建与使用分离,降低了系统的耦合度。

模式概述

工厂模式分为三种:

  1. 简单工厂模式(Simple Factory):不属于 GoF 23 种模式,但很常用
  2. 工厂方法模式(Factory Method):定义创建对象的接口,让子类决定实例化哪个类
  3. 抽象工厂模式(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

最佳实践

使用场景

简单工厂

  • 产品种类较少
  • 客户端不需要知道具体产品类
  • 不需要频繁扩展

工厂方法

  • 需要扩展产品种类
  • 需要将创建逻辑延迟到子类
  • 需要解耦客户端和具体产品

抽象工厂

  • 需要创建产品族
  • 需要保证产品族的一致性
  • 系统需要独立于产品的创建、组合和表示

实现建议

  1. 优先使用工厂方法:比简单工厂更灵活
  2. 结合配置文件:可以通过配置选择工厂
  3. 利用反射:可以进一步解耦
// 使用反射的工厂
public class ReflectiveFactory {
public static <T> T create(Class<T> clazz) {
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("创建实例失败", e);
}
}
}

小结

工厂模式是创建型模式的核心:

  1. 简单工厂:封装创建逻辑,适合产品种类少的场景
  2. 工厂方法:延迟到子类创建,符合开闭原则
  3. 抽象工厂:创建产品族,保证产品一致性

选择哪种工厂模式取决于具体需求:

  • 产品种类少 → 简单工厂
  • 需要扩展 → 工厂方法
  • 需要产品族 → 抽象工厂

练习

  1. 实现一个简单工厂,创建不同形状的对象
  2. 使用工厂方法模式实现一个文档解析器(支持 JSON、XML、YAML)
  3. 使用抽象工厂模式实现一个数据库访问层(支持 MySQL、PostgreSQL)
  4. 比较三种工厂模式的优劣

参考资源