抽象工厂模式 (Abstract Factory)
📖 通俗理解
想象你要装修房子,需要买一套家具:
- 如果选择现代风格:需要现代风格的沙发、茶几、电视柜
- 如果选择中式风格:需要中式风格的沙发、茶几、电视柜
你不会买一个现代沙发配一个中式茶几,那样会很奇怪!
抽象工厂模式就是:提供一个创建一系列相关对象的接口,而无需指定具体类。简单说就是——一个工厂生产一套产品。
🎯 工厂方法 vs 抽象工厂
| 模式 | 解决的问题 | 例子 |
|---|---|---|
| 工厂方法 | 生产一种产品 | 手机工厂只生产手机 |
| 抽象工厂 | 生产一系列产品 | 电子产品工厂生产手机+电脑+平板 |
🌰 生活中的例子
- 家具店:现代家具店卖整套现代家具,中式家具店卖整套中式家具
- 电脑品牌:苹果生产苹果手机+电脑+耳机,华为生产华为手机+电脑+耳机
- 汽车厂商:奔驰生产奔驰轿车+SUV+跑车,宝马生产宝马轿车+SUV+跑车
💻 Java 代码实现
场景:跨平台 UI 组件
我们要开发一个跨平台应用,需要适配 Windows 和 Mac 两个平台。每个平台都有按钮和输入框,但样式不同。
第一步:定义产品接口(产品族)
/**
* 按钮接口
*/
public interface Button {
void render(); // 渲染按钮
void onClick(); // 点击事件
}
/**
* 输入框接口
*/
public interface TextField {
void render(); // 渲染输入框
String getValue(); // 获取值
void setValue(String value); // 设置值
}
第二步:实现 Windows 平台的产品
/**
* Windows 风格按钮
*/
public class WindowsButton implements Button {
@Override
public void render() {
System.out.println("渲染 Windows 风格的按钮 [====]");
}
@Override
public void onClick() {
System.out.println("Windows 按钮被点击!");
}
}
/**
* Windows 风格输入框
*/
public class WindowsTextField implements TextField {
private String value = "";
@Override
public void render() {
System.out.println("渲染 Windows 风格的输入框 |______|");
}
@Override
public String getValue() {
return value;
}
@Override
public void setValue(String value) {
this.value = value;
}
}
第三步:实现 Mac 平台的产品
/**
* Mac 风格按钮
*/
public class MacButton implements Button {
@Override
public void render() {
System.out.println("渲染 Mac 风格的按钮 (====)");
}
@Override
public void onClick() {
System.out.println("Mac 按钮被点击!");
}
}
/**
* Mac 风格输入框
*/
public class MacTextField implements TextField {
private String value = "";
@Override
public void render() {
System.out.println("渲染 Mac 风格的输入框 |~~~~~~|");
}
@Override
public String getValue() {
return value;
}
@Override
public void setValue(String value) {
this.value = value;
}
}
第四步:定义抽象工厂
/**
* 抽象工厂接口
* 定义创建一系列产品的方法
*/
public interface GUIFactory {
Button createButton();
TextField createTextField();
}
第五步:实现具体工厂
/**
* Windows 工厂
* 生产一系列 Windows 风格的组件
*/
public class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public TextField createTextField() {
return new WindowsTextField();
}
}
/**
* Mac 工厂
* 生产一系列 Mac 风格的组件
*/
public class MacFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacButton();
}
@Override
public TextField createTextField() {
return new MacTextField();
}
}
第六步:客户端使用
public class Application {
private Button button;
private TextField textField;
public Application(GUIFactory factory) {
// 使用工厂创建一系列组件
this.button = factory.createButton();
this.textField = factory.createTextField();
}
public void render() {
button.render();
textField.render();
}
public void simulate() {
textField.setValue("Hello World");
button.onClick();
System.out.println("输入框的值: " + textField.getValue());
}
public static void main(String[] args) {
// 根据操作系统选择工厂
String os = System.getProperty("os.name").toLowerCase();
GUIFactory factory;
if (os.contains("windows")) {
factory = new WindowsFactory();
} else {
factory = new MacFactory();
}
Application app = new Application(factory);
app.render();
app.simulate();
}
}
Windows 下输出:
渲染 Windows 风格的按钮 [====]
渲染 Windows 风格的输入框 |______|
Windows 按钮被点击!
输入框的值: Hello World
Mac 下输出:
渲染 Mac 风格的按钮 (====)
渲染 Mac 风格的输入框 |~~~~~~|
Mac 按钮被点击!
输入框的值: Hello World
🔥 实战案例:数据库访问层
不同数据库(MySQL、Oracle)有不同的连接和命令实现。
/**
* 数据库连接接口
*/
public interface Connection {
void connect();
void disconnect();
}
/**
* 数据库命令接口
*/
public interface Command {
void execute(String sql);
}
// ========== MySQL 产品 ==========
public class MySQLConnection implements Connection {
@Override
public void connect() {
System.out.println("连接到 MySQL 数据库...");
}
@Override
public void disconnect() {
System.out.println("断开 MySQL 连接...");
}
}
public class MySQLCommand implements Command {
@Override
public void execute(String sql) {
System.out.println("MySQL 执行: " + sql);
}
}
// ========== Oracle 产品 ==========
public class OracleConnection implements Connection {
@Override
public void connect() {
System.out.println("连接到 Oracle 数据库...");
}
@Override
public void disconnect() {
System.out.println("断开 Oracle 连接...");
}
}
public class OracleCommand implements Command {
@Override
public void execute(String sql) {
System.out.println("Oracle 执行: " + sql);
}
}
// ========== 抽象工厂 ==========
public interface DatabaseFactory {
Connection createConnection();
Command createCommand();
}
public class MySQLFactory implements DatabaseFactory {
@Override
public Connection createConnection() {
return new MySQLConnection();
}
@Override
public Command createCommand() {
return new MySQLCommand();
}
}
public class OracleFactory implements DatabaseFactory {
@Override
public Connection createConnection() {
return new OracleConnection();
}
@Override
public Command createCommand() {
return new OracleCommand();
}
}
使用方式:
public class DatabaseClient {
private Connection connection;
private Command command;
public DatabaseClient(DatabaseFactory factory) {
this.connection = factory.createConnection();
this.command = factory.createCommand();
}
public void executeQuery(String sql) {
connection.connect();
command.execute(sql);
connection.disconnect();
}
public static void main(String[] args) {
// 使用 MySQL
DatabaseClient mysqlClient = new DatabaseClient(new MySQLFactory());
mysqlClient.executeQuery("SELECT * FROM users");
System.out.println("-------------------");
// 使用 Oracle
DatabaseClient oracleClient = new DatabaseClient(new OracleFactory());
oracleClient.executeQuery("SELECT * FROM users");
}
}
📊 类图结构
┌─────────────────────────────────────────────────────────────┐
│ AbstractFactory │
│ + createProductA(): AbstractProductA │
│ + createProductB(): AbstractProductB │
└─────────────────────────────────────────────────────────────┘
▲
┌──────────────┴──────────────┐
│ │
┌──────────┴──────────┐ ┌──────────┴──────────┐
│ ConcreteFactory1 │ │ ConcreteFactory2 │
│ + createProductA() │ │ + createProductA() │
│ + createProductB() │ │ + createProductB() │
└─────────────────────┘ └─────────────────────┘
│ │
创建ProductA1 创建ProductA2
创建ProductB1 创建ProductB2
✅ 适用场景
- 系统有多个产品族:如 Windows/Mac 的 UI 组件
- 产品族需要一起使用:如现代风格的沙发必须配现代风格的茶几
- 想隐藏产品的具体实现:客户端只和工厂接口打交道
⚠️ 优缺点
优点:
- 保证产品的一致性(同一工厂出来的产品风格统一)
- 客户端和具体产品解耦
- 符合开闭原则(新增产品族只需新增工厂)
缺点:
- 新增产品类型困难(需要修改所有工厂)
- 类的数量较多,增加系统复杂度
小结
抽象工厂模式的核心:一个工厂生产一系列相关的产品。
和工厂方法的区别:
- 工厂方法:一个工厂生产一种产品
- 抽象工厂:一个工厂生产一套产品
👉 下一篇:建造者模式
