1. 核心维度的区别
我们可以从两个维度来看待这个问题:
| 维度 | 工厂方法模式 (Factory Method) | 抽象工厂模式 (Abstract Factory) |
|---|---|---|
| 产品数量 | 单一产品 | 产品族 (Product Family) |
| 接口方法 | 只有一个 Create() 方法 |
有多个方法 CreateA(), CreateB()... |
| 关注点 | 只要把对象造出来就行,不需要考虑搭配 | 必须保证造出来的一堆对象是配套的,不能混用 |
| 维度举例 | 比如:只生产“枪” | 比如:生产“枪” + “子弹” + “瞄准镜” |
| 复杂度 | 较低 | 较高 |
2. 生动的比喻
A. 工厂方法模式 (Factory Method)
就像“单一品牌的专卖店”,但只卖一样东西。
- 需求: 你想买辆车。
- 奔驰工厂: 只负责造 奔驰车。
- 宝马工厂: 只负责造 宝马车。
- 客户端: 你找奔驰工厂,只能拿到奔驰车,拿不到别的。
B. 抽象工厂模式 (Abstract Factory)
就像“品牌全家桶”或者“套装装备”。
- 需求: 你不仅要买车,还要配套的钥匙、配套的轮胎。
- 奔驰工厂: 负责造 奔驰车 + 奔驰钥匙 + 奔驰轮胎。
- 宝马工厂: 负责造 宝马车 + 宝马钥匙 + 宝马轮胎。
- 客户端: 你找奔驰工厂,它会给你一整套奔驰的东西。
- 约束: 你绝对不会从奔驰工厂里拿到“宝马的钥匙”。(这就是抽象工厂的核心价值:防止混搭)
3. 代码结构的直观对比
工厂方法 (Factory Method)
接口里只有一个函数,生产一种东西。
class ICarFactory {
public:// 只有一个 Createvirtual ICar* CreateCar() = 0;
};// 具体工厂
class BenzFactory : public ICarFactory {ICar* CreateCar() override { return new BenzCar(); }
};
抽象工厂 (Abstract Factory)
接口里有一组函数,生产一系列相关的东西。
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()。 - 后果: 所有的具体工厂(奔驰工厂、宝马工厂...)全部都要修改代码来实现这个新函数。这严重违反了开闭原则。
- 你需要修改
总结:如何选择?
- 问自己:我需要创建的对象,是“孤立”的,还是“成套”的?
- 如果只是创建一个 Logger,或者一个 ThreadPool,用 工厂方法。
- 如果需要创建 GUI 皮肤(按钮+背景+滚动条必须风格一致),或者数据库组件(Connection+Command+Result必须全是 MySQL 的),用 抽象工厂。
- 大多数情况下,工厂方法模式就够用了。 抽象工厂由于太重,且接口难以修改,只有在确实有“产品族约束”时才使用。