【设计模式】抽象工厂模式(含与工厂方法模式的对比)

server/2025/4/2 5:46:05/

本期我们来学习一下设计模式抽象工厂模式,在软件开发中,工厂模式抽象工厂模式 都用于创建对象,但它们的应用场景和实现方式有所不同。本文将基于 C++ 代码,分析抽象工厂模式的实现,并对比其与工厂方法模式的区别。


1. 抽象工厂模式简介

抽象工厂模式(Abstract Factory Pattern)创建型设计模式,用于创建一系列相关或相互依赖的对象,而无需指定其具体类。它提供了一个接口,允许客户端通过工厂方法创建不同类型的对象,而无需关心具体实现。

适用场景

  • 当系统需要创建一系列相关的产品(例如同一风格的桌子和椅子)。
  • 希望确保不同产品之间的兼容性(即,现代风格的桌子应当搭配现代风格的椅子)。
  • 隐藏对象创建的细节,并遵循开放封闭原则

2. 代码分析:抽象工厂模式

我们先看一下接下来实现的示例代码UML图:
抽象工厂UML图

(1) 产品接口

我们定义了**椅子(Chair)桌子(Desk)**两个产品接口,并为它们的不同风格(现代、维多利亚)提供具体实现。

class Chair {
public:virtual ~Chair() {}virtual std::string SitOn() const = 0; // 坐在椅子上的行为
};class ModernChair : public Chair {
public:std::string SitOn() const override {return "Sitting on a modern chair.";}
};class VictorianChair : public Chair {
public:std::string SitOn() const override {return "Sitting on a Victorian chair.";}
};

类似地,我们定义了**桌子(Desk)**接口:

class Desk {
public:virtual ~Desk() {}virtual std::string WorkOn() const = 0; // 在桌子上工作的行为virtual std::string PairWithChair(const Chair &collaborator) const = 0; // 桌子与椅子配对
};

每种桌子都可以与同风格的椅子进行搭配:

class ModernDesk : public Desk {
public:std::string WorkOn() const override {return "Working on a modern desk.";}std::string PairWithChair(const Chair &collaborator) const override {return "Pairing modern desk with ( " + collaborator.SitOn() + " )";}
};

(2) 抽象工厂接口

class AbstractFactory {
public:virtual Chair *CreateChair() const = 0;virtual Desk *CreateDesk() const = 0;virtual ~AbstractFactory() {}
};

这个接口定义了创建相关产品的方法。

(3) 具体工厂

具体工厂负责生产特定风格的家具:

class ModernFurnitureFactory : public AbstractFactory {
public:Chair *CreateChair() const override {return new ModernChair();}Desk *CreateDesk() const override {return new ModernDesk();}
};
class VictorianFurnitureFactory : public AbstractFactory {
public:Chair *CreateChair() const override {return new VictorianChair();}Desk *CreateDesk() const override {return new VictorianDesk();}
};

每个工厂都会创建一组相关联的对象(现代风格 or 维多利亚风格)。

(4) 客户端代码

void ClientCode(const AbstractFactory &factory) {const Chair *chair = factory.CreateChair();const Desk *desk = factory.CreateDesk();std::cout << desk->WorkOn() << "\n";std::cout << desk->PairWithChair(*chair) << "\n";delete chair;delete desk;
}

客户端只与抽象工厂接口交互,而不需要知道具体的工厂实现。

int main() {ModernFurnitureFactory *f1 = new ModernFurnitureFactory();ClientCode(*f1);delete f1;VictorianFurnitureFactory *f2 = new VictorianFurnitureFactory();ClientCode(*f2);delete f2;return 0;
}

运行结果:

Client: Testing client code with the first factory type:
Working on a modern desk.
Pairing modern desk with ( Sitting on a modern chair. )Client: Testing the same client code with the second factory type:
Working on a Victorian desk.
Pairing Victorian desk with ( Sitting on a Victorian chair. )

3. 抽象工厂模式 vs. 工厂方法模式

比较项抽象工厂模式工厂方法模式
主要作用创建一系列相关对象仅创建单一对象
产品数量多个相关的产品(如桌子 + 椅子)单个产品
抽象程度提供多个工厂方法仅提供一个工厂方法
耦合性低,所有产品都由一个工厂创建,保证兼容性低,但每个产品类型需要一个工厂
适用场景需要确保产品之间的兼容性,例如 UI 组件仅创建某种特定类型的对象

示例代码对比

工厂方法模式

class ChairFactory {
public:static Chair* CreateChair() {return new ModernChair(); // 或者 VictorianChair}
};

工厂方法模式中,每个工厂只负责创建一个对象,而不是一组相关的对象。

抽象工厂模式

class AbstractFactory {
public:virtual Chair *CreateChair() const = 0;virtual Desk *CreateDesk() const = 0;
};

抽象工厂模式中,一个工厂负责创建一组产品(例如:现代风格的桌子 + 现代风格的椅子)。


4. 什么时候使用抽象工厂模式

当多个产品需要搭配使用时

  • 例如 GUI 库中,需要同时创建Windows 风格的按钮、窗口,或者Mac 风格的按钮、窗口

希望减少依赖并保持代码的可扩展性

  • 未来如果要新增 “复古风格家具”,只需创建新的 RetroFurnitureFactory,无需修改原有代码。

保证产品之间的兼容性

  • 例如,现代椅子只能搭配现代桌子,而不能搭配维多利亚桌子。

5. 总结

  • 工厂方法模式 只创建单一产品,而 抽象工厂模式 可以创建一组相关产品
  • 抽象工厂模式的优势 在于确保产品之间的兼容性,并降低客户端对具体类的依赖。
  • 适用于需要生产一系列相关对象的场景,例如 GUI 组件、数据库驱动等。

如果你的需求仅是创建单个对象,可以使用工厂方法模式
如果你的需求是创建多个相互关联的对象,建议使用抽象工厂模式


http://www.ppmy.cn/server/179834.html

相关文章

单片机串口打印调试信息①

在单片机开发中&#xff0c;通过串口&#xff08;UART&#xff09;输出调试信息是最常用的调试方法之一。以下是详细的操作指南&#xff0c;包括硬件连接、代码实现和调试信息规划策略&#xff1a; 一、硬件连接与配置 硬件准备&#xff1a; USB转TTL模块&#xff1a;连接单片机…

ue材质学习感想总结笔记

2025 - 3 - 27 1.1 加法 对TexCoord上的每一个像素加上一个值&#xff0c;如果加上0.1&#xff0c;0.1&#xff0c; 那么左上角原来0,0的位置变成了0.1,0.1 右上角就变成了1.1,1.1&#xff0c;那么原来0,0的位置就去到了左上角左上边&#xff0c;所以图像往左上偏移。 总而言…

redis常用部署架构之redis分片集群。

redis 3.x版本后开始支持 作用&#xff1a; 1.提升数据读写速度 2..提升可用性 分片集群就是将业务服务器产生的数据储存在不同的机器上。 redis分片集群的架构 如上图所示&#xff0c;会将数据分散存储到不同的服务器上&#xff0c;相比于之前来说&#xff0c;redis要处…

设计链表 LeetCode

pre1. 设计心得 无论是链表的增删改查某个节点&#xff0c;都可以通过找到该节点的前一个前一个节点实现。所以说&#xff0c;查找某个节点&#xff08;或者 i n d e x index index&#xff09;的前一个节点是一个很常用的操作&#xff0c;因此可以把它单独拿出来&#xff0c…

元能力的养成

问题&#xff1a;为什么好公司的人会自己走掉&#xff0c;差公司的人等着拿离职补偿。&#xff1f;&#xff1f; 优秀指向分离&#xff0c;愚昧指向忠诚&#xff0c;一个人的优秀&#xff0c;的确意味着他拥有更大的自由&#xff0c;意味着他比较容易和你分离&#xff0c;而走向…

自定义屏幕显示方向的实用软件

软件介绍 还在为手机或平板屏幕方向不受控制而烦恼吗&#xff1f;别担心&#xff0c;今天为大家带来一款堪称救星的神器 ——Rotation Pro。 被屏幕方向问题困扰&#xff1f; 在日常使用电子设备时&#xff0c;屏幕方向的问题常常让人抓狂。就拿平板办公来说&#xff0c;横屏…

详细比较StringRedisTemplate和RedisTemplate的区别及使用方法,及解决融合使用方法

前言 感觉StringRedisTemplate和RedisTemplate非常的相识&#xff0c;到底有什么区别和联系呢&#xff1f;点开idea&#xff0c;打开其依赖关系&#xff0c;可以看出只需使用maven依赖包spring-boot-starter-data-redis&#xff0c;然后在service中注入StringRedisTemplate或者…

栈-最小栈

155.最小栈 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。实现 MinStack 类:MinStack() 初始化堆栈对象。 void push(int val) 将元素val推入堆栈。 void pop() 删除堆栈顶部的元素。 int top() 获取堆栈顶部的元…