# 抽象工厂模式

# 概念

抽象工厂模式( Abstract Factory Pattern )是一种创建型设计模式,它围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂

抽象工厂模式提供了一种方式,可以创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂模式强调的是一个工厂类可以创建一组相关的产品对象,这些对象通常属于一个产品族。

# 作用

  1. 解耦客户端与具体产品:客户端代码只需要依赖抽象工厂和抽象产品,无需关心具体产品的创建和实现细节。

  2. 易于扩展产品族:如果需要支持新的产品族(如 Linux 风格的 UI 组件),只需添加一个新的具体工厂和具体产品,无需修改现有代码。

  3. 保证产品的一致性:通过工厂创建的一组产品是相互兼容的,属于同一个产品族。

# 场景

  1. 跨平台兼容:适配多平台(如 Windows/Mac ),提供风格一致的 UI 组件或服务。

  2. 产品族扩展:新增产品族(如 Linux UI )无需改代码,符合开闭原则。

  3. 强制产品兼容:确保对象配套使用(如统一游戏皮肤/硬件驱动)。

  4. 动态切换配置:运行时切换整组对象(如主题/支付网关)。

  5. 框架可插拔设计:框架支持第三方扩展(如 Spring 工厂/日志适配器)。

# 举例

假设我们正在开发一个跨平台的应用程序,需要为不同的操作系统(如 Windows 和 Mac )提供不同的UI组件。我们可以使用抽象工厂模式来创建这些UI组件。

抽象产品( Abstract Product ):

package net.feixiang.creational.abstractFactory;

/**
 * 按钮接口
 */
public interface Button {
    /**
     * 渲染按钮
     */
    void render();
}
package net.feixiang.creational.abstractFactory;

/**
 * 文本框接口
 */
public interface TextBox {
    /**
     * 渲染文本框
     */
    void render();
}

抽象工厂( Abstract Factory ):

package net.feixiang.creational.abstractFactory;

/**
 * 定义创建不同类型 GUI 组件的方法。具体的工厂类将实现这个接口来创建特定的组件。
 */
public interface GUIFactory {
    /**
     * 创建按钮
     *
     * @return 按钮
     */
    Button createButton();

    /**
     * 创建文本框
     *
     * @return 文本框
     */
    TextBox createTextBox();
}

具体产品( Concrete Product ):

package net.feixiang.creational.abstractFactory;

/**
 * Windows 风格的按钮实现
 */
public class WindowsButton implements Button {

    @Override
    public void render() {
        System.out.println("渲染 Windows 风格的按钮");
    }
}
package net.feixiang.creational.abstractFactory;

/**
 * Windows 风格的文本框实现
 */
public class WindowsTextBox implements TextBox {

    @Override
    public void render() {
        System.out.println("渲染 Windows 风格的文本框");
    }
}
package net.feixiang.creational.abstractFactory;

/**
 * Mac 风格的按钮实现
 */
public class MacButton implements Button {

    @Override
    public void render() {
        System.out.println("渲染 Mac 风格的按钮");
    }
}
package net.feixiang.creational.abstractFactory;

/**
 * Mac 风格的文本框实现
 */
public class MacTextBox implements TextBox {

    @Override
    public void render() {
        System.out.println("渲染 Mac 风格的文本框");
    }
}

具体工厂( Concrete Factory ):

package net.feixiang.creational.abstractFactory;

/**
 * Windows 风格的 GUI 工厂实现
 */
public class WindowsFactory implements GUIFactory {

    @Override
    public Button createButton() {
        System.out.println("创建 Windows 风格的按钮");
        return new WindowsButton();
    }

    @Override
    public TextBox createTextBox() {
        System.out.println("创建 Windows 风格的文本框");
        return new WindowsTextBox();
    }
}
package net.feixiang.creational.abstractFactory;

/**
 * Mac 风格的 GUI 工厂实现
 */
public class MacFactory implements GUIFactory {

    @Override
    public Button createButton() {
        System.out.println("创建 Mac 风格的按钮");
        return new MacButton();
    }

    @Override
    public TextBox createTextBox() {
        System.out.println("创建 Mac 风格的文本框");
        return new MacTextBox();
    }
}

运行示例:

package net.feixiang.creational.abstractFactory;

/**
 * 抽象工厂模式示例
 */
public class AbstractFactoryDemo {
    private final GUIFactory factory;

    public AbstractFactoryDemo(GUIFactory factory) {
        this.factory = factory;
    }

    /**
     * 创建用户界面
     * 根据不同的操作系统,创建不同风格的按钮和文本框
     */
    public void createUI() {
        Button button = factory.createButton();
        TextBox textBox = factory.createTextBox();
        button.render();
        textBox.render();
    }

    public static void main(String[] args) {
        // 输出当前操作系统
        System.out.println("当前操作系统: " + System.getProperty("os.name"));

        // 根据操作系统选择工厂
        GUIFactory factory;
        if (System.getProperty("os.name").contains("Windows")) {
            factory = new WindowsFactory();
        } else {
            factory = new MacFactory();
        }
        // 如果有其它系统,直接在这里新增实现类,不需要修改GUIFactory和createUI()
        // 例如:factory = new LinuxFactory();

        // 创建应用程序实例
        AbstractFactoryDemo app = new AbstractFactoryDemo(factory);
        app.createUI();
    }
}

控制台输出:

当前操作系统: Windows 11
创建 Windows 风格的按钮
创建 Windows 风格的文本框
渲染 Windows 风格的按钮
渲染 Windows 风格的文本框

# 反例

假设我们直接在客户端代码中创建 UI 组件,而不使用抽象工厂模式:

package net.feixiang.creational.abstractFactory.contrary;

import net.feixiang.creational.abstractFactory.*;

/**
 * 非抽象工厂模式示例
 * 这种方式不符合抽象工厂模式的设计原则,因为每次添加新的操作系统都需要
 * 修改 createUI() 方法,这会导致代码的可维护性和可扩展性变差。
 */
public class NonAbstractFactoryDemo {
    public void createUI() {
        Button button;
        TextBox textBox;

        if (System.getProperty("os.name").contains("Windows")) {
            button = new WindowsButton();
            textBox = new WindowsTextBox();
        } else {
            button = new MacButton();
            textBox = new MacTextBox();
        }
        // 如果有其它系统,这些需要新增 if else 代码,即需要更改 createUI() 方法

        button.render();
        textBox.render();
    }

    public static void main(String[] args) {
        // 输出当前操作系统
        System.out.println("当前操作系统: " + System.getProperty("os.name"));

        NonAbstractFactoryDemo app = new NonAbstractFactoryDemo();
        app.createUI();
    }
}

控制台输出:

当前操作系统: Windows 11
渲染 Windows 风格的按钮
渲染 Windows 风格的文本框

# 解析

  1. 代码耦合度高

    客户端代码直接依赖于具体产品类(如 WindowsButton、MacButton 等),如果需要添加新的产品族(如 Linux 风格的 UI 组件),必须修改客户端代码,违反了开闭原则。

  2. 难以维护和扩展

    每次添加新的产品族时,都需要在多个地方修改代码,容易引入错误。

  3. 无法保证产品的一致性

    如果客户端代码在不同地方创建了不一致的产品组合,可能会导致系统行为异常。

通过使用抽象工厂模式,可以有效解决这些问题,提高代码的可维护性和扩展性。


总结


抽象工厂模式作用是解耦客户端与具体产品,让客户端只依赖抽象;易于扩展产品族,新增产品族无需改现有代码;保证产品一致性,创建的产品相互兼容。
实现时先定义抽象工厂接口,声明创建相关产品的方法;再实现具体工厂类,针对不同产品族创建对应实例;定义抽象产品接口,声明通用行为;实现具体产品类;客户端通过抽象工厂动态选择具体工厂来创建一组相关产品。



微信公众号

QQ交流群
原创网站开发,偏差难以避免。

如若发现错误,诚心感谢反馈。

愿你倾心相念,愿你学有所成。

愿你朝华相顾,愿你前程似锦。