工厂模式和抽象工厂模式的区别

### 1. 核心维度的区别

我们可以从两个维度来看待这个问题:

| **维度** | **工厂方法模式 (Factory Method)** | **抽象工厂模式 (Abstract Factory)** |
| ———— | ———————————— | ———————————————- |
| **产品数量** | **单一产品** | **产品族 (Product Family)** |
| **接口方法** | 只有一个 `Create()` 方法 | 有多个方法 `CreateA()`, `CreateB()`… |
| **关注点** | 只要把对象造出来就行,不需要考虑搭配 | 必须保证造出来的一堆对象是**配套**的,不能混用 |
| **维度举例** | 比如:只生产“枪” | 比如:生产“枪” + “子弹” + “瞄准镜” |
| **复杂度** | 较低 | 较高 |

——

### 2. 生动的比喻

#### A. 工厂方法模式 (Factory Method)

就像**“单一品牌的专卖店”**,但只卖**一样东西**。

– **需求:** 你想买辆车。
– **奔驰工厂:** 只负责造 **奔驰车**。
– **宝马工厂:** 只负责造 **宝马车**。
– **客户端:** 你找奔驰工厂,只能拿到奔驰车,拿不到别的。

#### B. 抽象工厂模式 (Abstract Factory)

就像**“品牌全家桶”**或者**“套装装备”**。

– **需求:** 你不仅要买车,还要配套的钥匙、配套的轮胎。
– **奔驰工厂:** 负责造 **奔驰车** + **奔驰钥匙** + **奔驰轮胎**。
– **宝马工厂:** 负责造 **宝马车** + **宝马钥匙** + **宝马轮胎**。
– **客户端:** 你找奔驰工厂,它会给你**一整套**奔驰的东西。
– **约束:** 你绝对不会从奔驰工厂里拿到“宝马的钥匙”。**(这就是抽象工厂的核心价值:防止混搭)**

——

### 3. 代码结构的直观对比

#### 工厂方法 (Factory Method)

接口里只有一个函数,生产一种东西。

“`C++
class ICarFactory {
public:
// 只有一个 Create
virtual ICar* CreateCar() = 0;
};

// 具体工厂
class BenzFactory : public ICarFactory {
ICar* CreateCar() override { return new BenzCar(); }
};
“`

#### 抽象工厂 (Abstract Factory)

接口里有一组函数,生产一系列相关的东西。

“`C++
class ICarSuiteFactory {
public:
// 有一组 Create,它们是配套的
virtual ICar* CreateCar() = 0;
virtual IKey* CreateKey() = 0;
virtual ITire* CreateTire() = 0;
};

// 具体工厂
class BenzSuiteFactory : public ICarSuiteFactory {
ICar* CreateCar() override { return new BenzCar(); }
IKey* CreateKey() override { return new BenzKey(); } // 必须是奔驰钥匙
ITire* CreateTire() override { return new BenzTire(); } // 必须是奔驰轮胎
};
“`

### 4. 扩展性的区别 (开闭原则 OCP 的不同侧重)

这是最让架构师头疼的地方,两者的优缺点是互补的:

#### A. 工厂方法模式

– **增加新品牌(如奥迪):** **非常容易**。写一个 `AudiCar` 和 `AudiFactory` 即可。符合 OCP。
– **增加新产品(如造卡车):** **需要新建一套接口**。因为原来的工厂只造轿车。

#### B. 抽象工厂模式

– **增加新系列(如奥迪系列):** **非常容易**。写一个 `AudiSuiteFactory`,里面造奥迪车、奥迪钥匙。符合 OCP。
– **增加新产品(如增加“方向盘”):** **非常痛苦(灾难级)**。
– 你需要修改 `ICarSuiteFactory` 接口,增加 `CreateSteeringWheel()`。
– **后果:** 所有的具体工厂(奔驰工厂、宝马工厂…)全部都要修改代码来实现这个新函数。**这严重违反了开闭原则。**

——

### 总结:如何选择?

1. **问自己:我需要创建的对象,是“孤立”的,还是“成套”的?**
– 如果只是创建一个 Logger,或者一个 ThreadPool,用 **工厂方法**。
– 如果需要创建 GUI 皮肤(按钮+背景+滚动条必须风格一致),或者数据库组件(Connection+Command+Result必须全是 MySQL 的),用 **抽象工厂**。
2. **大多数情况下,工厂方法模式就够用了。** 抽象工厂由于太重,且接口难以修改,只有在确实有“产品族约束”时才使用。

发表评论