抽象工厂模式
核心思想回顾:
抽象工厂模式提供一个接口,用于创建一系列相关或相互依赖的对象(一个产品族),而客户端代码无需指定这些对象的具体类。这使得客户端代码与具体产品的实现解耦,并且可以轻松地切换整个产品族。
场景想象:
假设你要开发一个跨平台的 UI 库,这个库需要在 Windows 和 macOS 上都能运行,并且提供各自平台原生观感的按钮和复选框。
-
产品族1 (Windows): Windows 风格的按钮,Windows 风格的复选框。
-
产品族2 (macOS): macOS 风格的按钮,macOS 风格的复选框。
你希望你的应用程序代码在创建这些 UI 元素时,不需要关心当前运行的是哪个操作系统,只需要告诉工厂"给我一个按钮"和"给我一个复选框",工厂就能提供与当前平台匹配的组件。
模式的参与者 (Java 实现):
-
AbstractProduct (抽象产品):定义产品族中每种产品的接口。
-
Java: 通常是 interface。
-
例子:interface Button, interface Checkbox。
-
-
ConcreteProduct (具体产品):实现抽象产品接口,代表产品族中的具体产品。
-
Java: class 实现相应的 interface。
-
例子:class WindowsButton implements Button, class MacOSButton implements Button, class WindowsCheckbox implements Checkbox, class MacOSCheckbox implements Checkbox。
-
-
AbstractFactory (抽象工厂):声明一组用于创建抽象产品的方法。
-
Java: 通常是 interface。
-
例子:interface GUIFactory { Button createButton(); Checkbox createCheckbox(); }。
-
-
ConcreteFactory (具体工厂):实现抽象工厂接口,负责创建特定产品族中的具体产品。
-
Java: class 实现 GUIFactory 接口。
-
例子:class WindowsFactory implements GUIFactory, class MacOSFactory implements GUIFactory。
-
-
Client (客户端):仅使用 AbstractFactory 和 AbstractProduct 接口。
-
Java: 一个使用 GUIFactory 来创建 UI 组件的类。
-
例子:class Application。
-
Java 示例代码:
// 1. AbstractProduct (抽象产品接口)
interface Button {
void paint();
}
interface Checkbox {
void paint();
}
// 2. ConcreteProduct (具体产品 - Windows 风格)
class WindowsButton implements Button {
@Override
public void paint() {
System.out.println("Rendering a Windows style button.");
}
}
class WindowsCheckbox implements Checkbox {
@Override
public void paint() {
System.out.println("Rendering a Windows style checkbox.");
}
}
// 2. ConcreteProduct (具体产品 - macOS 风格)
class MacOSButton implements Button {
@Override
public void paint() {
System.out.println("Rendering a macOS style button.");
}
}
class MacOSCheckbox implements Checkbox {
@Override
public void paint() {
System.out.println("Rendering a macOS style checkbox.");
}
}
// 3. AbstractFactory (抽象工厂接口)
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// 4. ConcreteFactory (具体工厂 - Windows 工厂)
class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
// 4. ConcreteFactory (具体工厂 - macOS 工厂)
class MacOSFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacOSButton();
}
@Override
public Checkbox createCheckbox() {
return new MacOSCheckbox();
}
}
// 5. Client (客户端)
class Application {
private Button button;
private Checkbox checkbox;
private GUIFactory factory;
public Application(GUIFactory factory) {
this.factory = factory;
}
public void createUI() {
// 客户端不关心具体是哪个工厂,也不关心具体的产品类型
// 它只知道从工厂获取 Button 和 Checkbox 的抽象
this.button = factory.createButton();
this.checkbox = factory.createCheckbox();
}
public void paintUI() {
if (button != null && checkbox != null) {
button.paint();
checkbox.paint();
} else {
System.out.println("UI components not created yet. Call createUI() first.");
}
}
}
// 主程序,用于演示
public class AbstractFactoryDemo {
// 辅助方法,根据配置或环境决定使用哪个工厂
private static Application configureApplication() {
GUIFactory factory;
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("win")) {
factory = new WindowsFactory();
System.out.println("Configuring for Windows OS.");
} else if (osName.contains("mac")) {
factory = new MacOSFactory();
System.out.println("Configuring for macOS.");
} else {
// 默认或不支持的操作系统,可以抛出异常或使用默认工厂
System.out.println("OS not specifically supported, defaulting to Windows style.");
factory = new WindowsFactory();
}
return new Application(factory);
}
public static void main(String[] args) {
// 根据当前环境配置应用程序
Application app = configureApplication();
app.createUI();
app.paintUI();
System.out.println("\n--- Manually switching to MacOS factory for demonstration ---");
// 强制使用 macOS 工厂进行演示
Application macApp = new Application(new MacOSFactory());
macApp.createUI();
macApp.paintUI();
System.out.println("\n--- Manually switching to Windows factory for demonstration ---");
// 强制使用 Windows 工厂进行演示
Application winApp = new Application(new WindowsFactory());
winApp.createUI();
winApp.paintUI();
}
}
代码解释:
-
Button 和 Checkbox 是抽象产品接口。
-
WindowsButton, WindowsCheckbox, MacOSButton, MacOSCheckbox 是具体产品实现。
-
GUIFactory 是抽象工厂接口,它声明了创建 Button 和 Checkbox 的方法。
-
WindowsFactory 和 MacOSFactory 是具体工厂,它们分别实现了 GUIFactory,用于创建对应平台的 UI 组件。
-
Application 是客户端。它的构造函数接收一个 GUIFactory。在 createUI() 方法中,它使用这个工厂来创建按钮和复选框,而不需要知道具体是哪个工厂或哪个产品。
-
AbstractFactoryDemo.configureApplication() 方法模拟了根据操作系统选择合适工厂的逻辑。
-
main 方法展示了如何使用这个模式。客户端 (Application) 的代码是通用的,只需要在初始化时传入不同的工厂实例,就能得到不同风格的 UI 组件。
优点:
-
隔离具体类:客户端代码与具体产品的创建逻辑解耦,只依赖于抽象接口。
-
易于交换产品族:改变应用程序使用的产品族非常容易,只需要更换具体工厂的实例即可。例如,从 WindowsFactory 切换到 MacOSFactory。
-
保证产品兼容性:因为一个具体工厂只创建属于同一个产品族的产品,所以可以保证客户端创建出来的产品是相互兼容的(例如,Windows 按钮和 Windows 复选框搭配)。
-
符合开闭原则(对扩展开放,对修改关闭):当需要支持一个新的产品族(比如 Linux GTK 风格)时,只需要添加新的具体产品类(GTKButton, GTKCheckbox)和新的具体工厂类(GTKFactory),而不需要修改现有的工厂接口或客户端代码。
缺点:
- 难以增加新的产品种类:如果要在抽象工厂接口中增加一个新的抽象产品(比如 createTextField()),那么所有的具体工厂类都需要修改以实现这个新方法。这违反了开闭原则。对于这种情况,可能需要考虑其他模式,或者对抽象工厂进行扩展。
何时使用:
-
当你的系统需要独立于其产品的创建、组合和表示方式时。
-
当你的系统需要配置多个产品系列中的一个时。
-
当你希望强制用户使用同一产品系列中的对象,以保证兼容性时。
-
当你提供一个产品库,并且只想暴露其接口,隐藏其具体实现时。
抽象工厂模式是处理复杂对象创建,特别是需要保证产品族一致性时的强大工具。