【软考设计模式】抽象工厂模式:产品族构建与代码填空精讲

软考设计模式系列第 2 篇。上一篇我们聊了简单工厂模式 ------用一个工厂类根据参数"按需分配"具体产品。但简单工厂有个局限:它只能生产单一产品等级 (比如只生产按钮,或只生产文本框)。如果系统需要同时生产一组配套的产品 (比如 Windows 风格的按钮 + Windows 风格的文本框,或 Mac 风格的按钮 + Mac 风格的文本框),简单工厂就力不从心了。这时,抽象工厂模式登场。


一、模式定义

抽象工厂模式(Abstract Factory Pattern) 是一种创建型设计模式,它为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类

通俗地说:抽象工厂不生产"单个产品",它生产"整套产品族"。

1.1 核心概念:产品族 vs 产品等级

概念 含义 举例
产品等级结构 同一类产品的继承结构 按钮 ← Windows按钮 / Mac按钮
产品族 同一具体工厂生产的所有产品 Windows工厂:Windows按钮 + Windows文本框 + Windows组合框

抽象工厂解决的核心问题是:保证同一个产品族中的对象被一起使用,避免 Windows 按钮配 Mac 文本框这种"混搭"问题。


二、UML 类图与角色

复制代码
┌─────────────────────┐
│  <<interface>>      │
│   AbstractFactory   │
│ + createButton()    │
│ + createTextField() │
└──────────┬──────────┘
           │
    ┌──────┴──────┐
    ▼             ▼
┌───────────┐  ┌───────────┐
│ WinFactory│  │ MacFactory│
│(具体工厂1) │  │(具体工厂2  │
└────┬──────┘  └────┬──────┘
     │             │
     ▼             ▼
┌─────────────┐ ┌─────────────┐
│<<interface>>│ │<<interface>>│
│  Button     │ │ TextField   │
│ + draw()    │ │ + display() │
└────┬────────┘ └─────┬───────┘
     │                │
   ┌─┴──┐           ┌─┴──┐
   ▼    ▼           ▼    ▼
┌──────┐┌──────┐ ┌─────┐┌─────┐
│WinBtn││MacBtn│ │WinTF││MacTF│
└──────┘└──────┘ └─────┘└─────┘

角色说明

角色 职责
AbstractFactory 抽象工厂接口,声明创建各个产品的方法
ConcreteFactory 具体工厂(如 WinFactory、MacFactory),实现抽象工厂接口,生产同一产品族的所有对象
AbstractProduct 抽象产品接口(如 Button、TextField)
ConcreteProduct 具体产品(如 WinButton、MacButton)
Client 客户端,通过抽象工厂和抽象产品接口操作,不依赖具体类

三、Java 代码实现

以"跨平台 UI 组件库"为例,实现 Windows 和 Mac 两个产品族。

3.1 抽象产品接口

java 复制代码
// 抽象产品:按钮
public interface Button {
    void draw();
}

// 抽象产品:文本框
public interface TextField {
    void display();
}

3.2 具体产品(Windows 族)

java 复制代码
public class WinButton implements Button {
    @Override
    public void draw() {
        System.out.println("绘制 Windows 风格按钮");
    }
}

public class WinTextField implements TextField {
    @Override
    public void display() {
        System.out.println("显示 Windows 风格文本框");
    }
}

3.3 具体产品(Mac 族)

java 复制代码
public class MacButton implements Button {
    @Override
    public void draw() {
        System.out.println("绘制 Mac 风格按钮");
    }
}

public class MacTextField implements TextField {
    @Override
    public void display() {
        System.out.println("显示 Mac 风格文本框");
    }
}

3.4 抽象工厂

java 复制代码
public interface GUIFactory {
    Button createButton();
    TextField createTextField();
}

3.5 具体工厂

java 复制代码
public class WinFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WinButton();
    }
    
    @Override
    public TextField createTextField() {
        return new WinTextField();
    }
}

public class MacFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new MacButton();
    }
    
    @Override
    public TextField createTextField() {
        return new MacTextField();
    }
}

3.6 客户端代码

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 通过配置或参数决定使用哪个产品族
        String osType = "Windows"; // 或 "Mac"
        
        GUIFactory factory;
        if (osType.equals("Windows")) {
            factory = new WinFactory();
        } else {
            factory = new MacFactory();
        }
        
        // 客户端只依赖抽象接口
        Button btn = factory.createButton();
        TextField tf = factory.createTextField();
        
        btn.draw();
        tf.display();
        
        // 输出:绘制 Windows 风格按钮
        //      显示 Windows 风格文本框
    }
}

四、软考代码填空常见考法

软考下午题中,抽象工厂模式通常以代码填空形式出现,重点考察以下位置:

典型填空点 1:抽象工厂接口声明

java 复制代码
public interface GUIFactory {
    Button (1);        // 创建按钮
    TextField (2);     // 创建文本框
}

答案: (1) createButton() (2) createTextField()

典型填空点 2:具体工厂实现

java 复制代码
public class WinFactory implements GUIFactory {
    public Button createButton() {
        return (3);    // 填空
    }
}

答案: (3) new WinButton()

典型填空点 3:客户端依赖关系

java 复制代码
public class Client {
    private (4) factory;  // 声明类型
    private Button btn;
    
    public Client((5) factory) {  // 参数类型
        this.factory = factory;
    }
}

答案: (4) GUIFactory (5) GUIFactory

软考核心原则 :客户端应依赖抽象接口(GUIFactory / Button),而非具体类(WinFactory / WinButton)。


五、抽象工厂 vs 工厂方法 vs 简单工厂

表格

维度 简单工厂 工厂方法 抽象工厂
产品数量 单一产品等级 单一产品等级 多个产品等级(产品族)
工厂数量 一个工厂类 一个产品对应一个工厂 一个产品族对应一个工厂
扩展性 新增产品需改工厂 新增产品等级只需加工厂 新增产品族只需加工厂;新增产品等级需改所有工厂
复杂度
软考考频

关键区别记忆口诀

工厂方法 :一个工厂造"一种东西"的不同款式;

抽象工厂:一个工厂造"一套东西"的配套款式。


六、优缺点分析

优点

  1. 产品族一致性:同一工厂生产的对象一定是兼容的,不会出现风格混搭。

  2. 隔离具体类:客户端只依赖抽象接口,符合依赖倒置原则。

  3. 易于切换产品族 :将 WinFactory 换成 MacFactory,整套 UI 风格统一切换。

缺点

  1. 扩展产品等级困难 :如果要在产品族中新增"下拉框(ComboBox)",需要修改所有具体工厂类(WinFactory、MacFactory...),违反开闭原则。

  2. 类数量膨胀:N 个产品族 × M 个产品等级 = 大量类文件。


七、适用场景

  1. 系统需要独立于产品的创建、组合和表示(如跨平台 UI 框架)。

  2. 系统需要由多个产品族中的一个来配置(如不同数据库厂商:MySQL 族 / Oracle 族)。

  3. 需要强调一系列相关产品的配套使用(如游戏开发中的"精灵族":精灵建筑 + 精灵兵种 + 精灵魔法)。


八、软考备考要点总结

表格

考点 要点
定义填空 "为创建一组相关或相互依赖的对象提供接口,无需指定具体类"
角色识别 能区分 AbstractFactory / ConcreteFactory / AbstractProduct / ConcreteProduct
代码填空 工厂返回值类型为具体产品 ;客户端声明类型为抽象产品/抽象工厂
与工厂方法区别 抽象工厂处理产品族 (多个产品等级);工厂方法处理单一产品等级
扩展性陷阱 新增产品族容易(加工厂);新增产品等级难(改所有工厂)------这是软考常考的缺点

九、完整记忆图

java 复制代码
        抽象工厂 GUIFactory
            │
    ┌───────┴───────┐
    ▼               ▼
 WinFactory      MacFactory  ← 产品族(容易扩展:新增 LinuxFactory)
    │               │
    ├─Button        ├─Button
    │   │           │   │
    │  WinButton    │  MacButton
    │               │
    ├─TextField     ├─TextField
    │   │           │   │
    │  WinTF           MacTF
    │
    └─ComboBox?     ← 新增产品等级(困难:所有工厂都要改)

十、结语

抽象工厂模式是软考下午题中出镜率极高 的创建型模式。掌握它的关键在于理解**"产品族"** 这个概念------它不是生产一个对象,而是生产一套配套对象 。记住它的优缺点(扩展产品族易、扩展产品等级难),分清它与工厂方法模式的边界,代码填空时留意**"抽象类型声明,具体类型实例化"**这一原则,软考中遇到抽象工厂的题目就能从容应对。