一、工厂方法模式的局限性
工厂方法模式仅能创建单一产品等级结构的对象,当系统需要创建多个相互关联的产品族时,工厂方法模式便显现出明显不足。例如,在开发跨平台 UI 组件时,需要同时创建 Windows 风格的按钮和文本框,以及 macOS 风格的按钮和文本框,这些按钮和文本框分别构成不同的产品族。若使用工厂方法模式,需为每个产品(按钮、文本框)都创建对应的工厂类,会导致类的数量急剧增加,且难以保证同一产品族内产品的一致性。而抽象工厂模式恰好能解决这一问题,它可提供一个接口,用于创建一系列相关或相互依赖的对象,无需指定它们的具体类。
二、抽象工厂模式结构
2.1 结构文字描述
抽象工厂模式包含四个核心角色:
抽象工厂(Abstract Factory):声明一组用于创建一族产品的方法,每个方法对应一种产品。
具体工厂(Concrete Factory):实现抽象工厂中声明的创建产品的方法,生成具体的产品对象,一个具体工厂对应一个产品族。
抽象产品(Abstract Product):为每种产品声明接口,定义产品的公共方法。
具体产品(Concrete Product):实现抽象产品接口,是具体工厂创建的目标对象,一个具体产品属于某个具体工厂对应的产品族。
2.2 流程图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| +-------------------+ | 抽象工厂 | | (AbstractFactory)| +-------------------+ | | 声明创建产品A、产品B的方法 | +----------------------+----------------------+ | | | +-------------------+ +-------------------+ +-------------------+ | 具体工厂1 | | 具体工厂2 | | 具体工厂3 | | (ConcreteFactory1)| | (ConcreteFactory2)| | (ConcreteFactory3)| +-------------------+ +-------------------+ +-------------------+ | | | | 实现创建产品A1、 | 实现创建产品A2、 | 实现创建产品A3、 | 产品B1的方法 | 产品B2的方法 | 产品B3的方法 | | | +-------------------+ +-------------------+ +-------------------+ | 具体产品A1 | | 具体产品A2 | | 具体产品A3 | | (ConcreteProductA1)| | (ConcreteProductA2)| | (ConcreteProductA3)| +-------------------+ +-------------------+ +-------------------+ | | | | 实现产品A接口 | 实现产品A接口 | 实现产品A接口 | | | +-------------------+ +-------------------+ +-------------------+ | 具体产品B1 | | 具体产品B2 | | 具体产品B3 | | (ConcreteProductB1)| | (ConcreteProductB2)| | (ConcreteProductB3)| +-------------------+ +-------------------+ +-------------------+ | | | | 实现产品B接口 | 实现产品B接口 | 实现产品B接口
|
三、完整实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| #include <iostream> #include <memory> #include <string>
// 抽象产品A:按钮 class Button { public: virtual ~Button() = default; virtual void display() const = 0; };
// 抽象产品B:文本框 class TextBox { public: virtual ~TextBox() = default; virtual void display() const = 0; };
// 具体产品A1:Windows风格按钮 class WindowsButton : public Button { public: void display() const override { std::cout << "显示Windows风格按钮" << std::endl; } };
// 具体产品B1:Windows风格文本框 class WindowsTextBox : public TextBox { public: void display() const override { std::cout << "显示Windows风格文本框" << std::endl; } };
// 具体产品A2:macOS风格按钮 class MacOSButton : public Button { public: void display() const override { std::cout << "显示macOS风格按钮" << std::endl; } };
// 具体产品B2:macOS风格文本框 class MacOSTextBox : public TextBox { public: void display() const override { std::cout << "显示macOS风格文本框" << std::endl; } };
// 抽象工厂:UI组件工厂 class UIFactory { public: virtual ~UIFactory() = default; virtual std::unique_ptr<Button> createButton() = 0; virtual std::unique_ptr<TextBox> createTextBox() = 0; };
// 具体工厂1:Windows UI组件工厂 class WindowsUIFactory : public UIFactory { public: std::unique_ptr<Button> createButton() override { return std::make_unique<WindowsButton>(); }
std::unique_ptr<TextBox> createTextBox() override { return std::make_unique<WindowsTextBox>(); } };
// 具体工厂2:macOS UI组件工厂 class MacOSUIFactory : public UIFactory { public: std::unique_ptr<Button> createButton() override { return std::make_unique<MacOSButton>(); }
std::unique_ptr<TextBox> createTextBox() override { return std::make_unique<MacOSTextBox>(); } };
// 客户端代码 void clientCode(const std::unique_ptr<UIFactory>& factory) { auto button = factory->createButton(); auto textbox = factory->createTextBox(); button->display(); textbox->display(); }
int main() { std::cout << "客户端使用Windows UI组件工厂:" << std::endl; auto windowsFactory = std::make_unique<WindowsUIFactory>(); clientCode(windowsFactory);
std::cout << "\n客户端使用macOS UI组件工厂:" << std::endl; auto macOSFactory = std::make_unique<MacOSUIFactory>(); clientCode(macOSFactory);
return 0; }
|
四、模式总结
4.1 适用场景
系统需要创建多个相互关联的产品族,且需保证同一产品族内产品的一致性。
系统不依赖于具体产品的创建、组合和表示细节,客户端仅通过抽象接口访问产品。
系统需动态切换不同的产品族,如跨平台应用中切换不同系统风格的组件。
4.2 常见误区
过度使用抽象工厂模式:当系统仅需创建单一产品等级结构的对象时,使用工厂方法模式即可,无需使用抽象工厂模式,避免增加系统复杂度。
产品族扩展困难:抽象工厂模式中,若需新增产品族中的产品类型(如在 UI 组件中新增下拉框),需修改抽象工厂接口及所有具体工厂类,违反开闭原则,因此在设计时需提前明确产品族的范围。
忽视智能指针的使用:在 C++ 实现中,若未使用智能指针(如 std::unique_ptr)管理产品对象的生命周期,易出现内存泄漏问题,需注重资源的正确管理。