精读《C++20设计模式》:创造性模式------工厂方法和抽象工厂模式
前言
上一篇中,咱们仔细聊了聊经典的构造器模式。很显然,我们将构造函数的繁杂的职责交给了一个外置的类:构造器Builder来让Builder完成对Object的构造。而不是让Object自己承担了所有的构造点。我们还有一种设计模式。那就是这个标题中我们已经摆在这里的,就是工厂方法和抽象工厂模式。
毫无疑问,他们都在试图解决咱们的对象构建的议题。但是我们已经有了构建器模式了,为什么还需要工厂模式呢?不要着急。我们下面会采用办法来展开说明为什么我们需要工厂模式,和工厂模式到底好在哪里。
先别着急,坐下来陪我吃点汉堡
嘿!我们不要砌墙了!吃个汉堡吧!
Tips:原文中是拿造墙举例子的,不得不承认,我看了好几次了,看到最后好像才明白一点,但是也不是很明白,这里笔者打算换一个例子,咱们吃汉堡吧!
现在我们来到一个汉堡店。在路上我们就商量好了,说我们各点一个汉堡,然后爽吃它。
c++
struct Burger
{
// Burgers we support
enum class BurgerType {
Cheese,
Beef,
Chicken
};
};
struct Person {
Person() = default;
Burger::BurgerType preferType;
void enjoy_burger(const Burger* aBurger);
// omitted others
};
void enjoy_our_meals(vector<Person>& crowds)
{
for(auto& each_person : crowds){
// followings will promise the pBurger is assigned.
Burger* pBurger = nullptr;
switch(each_person.preferType){
case Burger::BurgerType::Cheese:
// We are making a cheese burger, details are exposed here
break;
... // oh shit we need to make thousands of burgers
}
each_person.enjoy_burger(pBurger);
}
}
enjoy_our_meals显然就已经良好的把我们的事情交代清楚了,但是交代的优雅嘛?不算优雅,我们把根据我们每个人的口味创建汉堡的流程暴露在了其实跟我们爽吃汉堡这个流程关系不大的吃饭函数里了。
最简单的工厂模式
我们很快的看出来了猫腻。任何一个写过代码的人就会指责这段代码甚至没有做最基本的封装。我们完全可以藏起来创建汉堡的流程。你看:
cpp
Burger* createBurgerFromPreference(const Person& person)
{
Burger* pBurger = nullptr;
// followings will promise the pBurger is assigned.
switch(person.preferType){
case Burger::BurgerType::Cheese:
// We are making a cheese burger, details are exposed here
break;
... // oh shit we need to make thousands of burgers
}
return pBurger;
}
void enjoy_our_meals(vector<Person>& crowds)
{
for(auto& each_person : crowds){
each_person.enjoy_burger(createBurgerFromPreference(each_person));
}
}
最好的是,之后如果我们的汉堡世界更加的丰富多彩了,我们完全没有任何负担的去添加或者减少我们的汉堡数量,甚至我们可以大胆的说,enjoy_our_meals一行都不用改。我们完全看不到汉堡出现在这里!
一般而言,做到这里就结束了,但是你知道的,这里是设计模式,我们的主角还没有出现。如果我们将函数createBurgerFromPreference
封装成一个静态的工具类,这个小工具就会辛劳的创建汉堡,并且将汉堡的控制权转移到我们手上吃掉。这就是工厂模式最开始的雏形。或者,你就叫他工厂模式的核心:工厂类吧!
我们把代码整理一下,这里我们将createBurgerFromPreference
退居到一个静态方法中。
cpp
struct BurgerFactory {
/**
* @brief Create a Burger From Preference by person
*
* @param person
* @return Burger*
*/
static Burger* createBurgerFromPreference(const Person& person);
};
Burger* BurgerFactory::createBurgerFromPreference(const Person& person) {
switch (person.preferType) {
case Burger::BurgerType::Beef:
return new BeefBurger; // 这些汉堡都在别处定义了,详细的代码参考笔者的Github源代码仓库!
case Burger::BurgerType::Cheese:
return new CheessBurger;
case Burger::BurgerType::Chicken:
return new ChickenBurger;
}
return nullptr;
}
现在我们就可以开始正式入门工厂模式了
cpp
#include "./burger.h"
#include "./person.hpp"
#include <vector>
void enjoy_our_meals(std::vector<Person>& crowds) {
for (auto& each_person : crowds) {
each_person.enjoy_burger(BurgerFactory::createBurgerFromPreference(each_person));
}
}
int main() {
std::vector<Person> persons {
{}, { Burger::BurgerType::Cheese }, { Burger::BurgerType::Chicken }
};
enjoy_our_meals(persons);
}
[charliechen@Charliechen design_pattern]$ ./build/Document_Demo/Factory/DemoFactory/DemoFactory
The Person is enjoying the burger:
A burger with beeeeeeeeeeeeef!
Yami!
The Person is enjoying the burger:
A burger with cheese!
Yami!
The Person is enjoying the burger:
A burger with chicken!
Yami!
所以,工厂模式在干什么
我们看到,我们的场景是希望将构造对象实现细节无关的执行代码剥离出去,或者说------希望在构造后做一些共同的执行逻辑。这个时候,我们往往选择将创建子类的具体行为放置到一个专门决定我们需要如何创建子类的函数或者是类中完成这个工作。这样的接口往往返回的是对象的基类,这就返回到我们上面说的------后续代码的细节跟具体何种子类无关,需要操作的是其他跟这个父类有关的内容。
我们回过头来,实际上我们刚刚就是这样做的------将创建汉堡的细节放置到了BurgerFactory中,他会返回一个通用的汉堡,然后我们吃了汉堡(Yami!)。这就是工厂模式的第一个用途了!
工厂模式的一个重要用途是------他针对存在一个复杂派生体系的对象蔟,我们可以将创建详细子类的细节封装到一个工厂中藏起来,返回出来一个共同的基类完成跟基类相关的操作,而不需要暴露出来子类的实现细节。或者说:工厂模式 (Factory Pattern)关注的是"生产什么"。它像一个代工厂,你告诉它你想要A产品还是B产品,它直接把成品给你。它隐藏了创建对象的具体类别,你只需要关心最终得到的对象接口。
进一步改进:更加智能的抽象/嵌套工厂
我们注意到,我们的一个工厂还是在掌管所有的汉堡的制作方法。如果我们后面的汉堡选择有更加复杂的决策策略,或者是我们的汉堡非常的复杂,一个工厂掌管所有的构造细节还是有一些太吃力了。
为此,我们可以进一步划分职责------我们的BurgerFactory
是顶层工厂(入口工厂),它不直接自己做所有的 Burger
实例,而是持有若干 "子工厂"(ConcreteBurgerFactory
),每个子工厂专门负责某一类 Burger
的创建。ConcreteBurgerFactory
是抽象基类(接口),定义 create(BurgerType)
或其它方法,由子类具体实现。
那问题来啦,如何选择正确的工厂呢?答案是给BurgerFactory
添加一个注册机制(工厂注册表/映射表),顶层工厂根据某种 key(例如 Person
的偏好)选一个子工厂,再让它来造具体的 Burger
。
这样做的好处是:顶层工厂和使用者都不需要了解所有具体 Burger
类、也不需要写庞大的 switch/if else;新增一种产品类型或子工厂,只需新增子工厂类 + 注册代码即可。
cpp
struct ConcreteBurgerFactory {
virtual ~ConcreteBurgerFactory() = default;
virtual std::unique_ptr<Burger> create(Burger::BurgerType p) const = 0;
};
struct BurgerFactory {
using FactoryCreator = std::function<std::unique_ptr<ConcreteBurgerFactory>()>;
static void registerFactory(const std::string& key, FactoryCreator c) {
getRegistry()[key] = c;
}
static std::unique_ptr<Burger> createBurgerFromPreference(const Person& person);
private:
static std::map<std::string, FactoryCreator>& getRegistry() {
static std::map<std::string, FactoryCreator> registry;
return registry;
}
static std::string chooseFactoryKey(const Person& person);
};
看到了嘛?我们实际上将最为具体的ConcreteBurgerFactory(也就是直接对接汉堡的具体工厂)交给BurgerFactory进行托管,BurgerFactory本身只是调度谁来负责产生产品,注意到的是createBurgerFromPreference可以改回Burger*,但是笔者建议RAII还是更好一些。
cpp
struct SimpleBurgerFactory : public ConcreteBurgerFactory {
std::unique_ptr<Burger> create(Burger::BurgerType p) const override {
switch (p) {
case Burger::BurgerType::Cheese:
return std::make_unique<CheeseBurger>();
case Burger::BurgerType::Beef:
return std::make_unique<BeefBurger>();
case Burger::BurgerType::Chicken:
return std::make_unique<ChickenBurger>();
default:
throw std::runtime_error("Unknown burger preference");
}
}
};
struct HealthyBurgerFactory : public ConcreteBurgerFactory {
std::unique_ptr<Burger> create(Burger::BurgerType p) const override {
if (p == Burger::BurgerType::Beef) {
return std::make_unique<ChickenBurger>();
}
// 其余不变
return std::make_unique<CheeseBurger>(); // 简化示例:都做 CheeseBurger
}
};
最重要的是,我们甚至可以添加新的创建策略,比如说吃饭的几个人要健康,那咱们就创建一个HealthyBurgerFactory,作为真正的调度策略类。
这种方式是嵌套 + 抽象工厂模式,也就是说------让具体的子类对接产品的制作细节,大工厂只是根据客户的选择翻译到底委托哪一个更加具体的工厂执行任务。
还能更进一步------函数式工厂
我们发现,这些工厂本质上是静态函数的封装,咱们要不要更进一步,让函数直接作为我们生产调度的主体,因为我们这样做------就可以使用lambda作为咱们的工厂策略了。
cpp
#include <functional>
#include <unordered_map>
#include <stdexcept>
// 工厂函数的类型别名
using BurgerFactoryFunc = std::function<std::unique_ptr<Burger>(Burger::BurgerType)>;
// 一个注册表:把 key 映射到工厂函数
class FunctionalBurgerFactory {
public:
// 注册某个 key 对应的工厂函数
static void registerFactory(const std::string& key, BurgerFactoryFunc f) {
getRegistry()[key] = std::move(f);
}
// 用某个 key 创建 burger;如果失败则抛异常
static std::unique_ptr<Burger> createBurger(const std::string& key, Burger::BurgerType p) {
auto& reg = getRegistry();
auto it = reg.find(key);
if (it == reg.end()) {
throw std::runtime_error("No factory registered for key = " + key);
}
// 调用注册的函数
return (it->second)(p);
}
private:
static std::unordered_map<std::string, BurgerFactoryFunc>& getRegistry() {
static std::unordered_map<std::string, BurgerFactoryFunc> registry;
return registry;
}
};
现在,我们不用注册复杂的子类了,而是直接注册对应的创建函数。
cpp
void initBurgerFactories() {
// "simple" 风格工厂:直接映射偏好到具体 burger
FunctionalBurgerFactory::registerFactory("simple",
[](Burger::BurgerType p) -> std::unique_ptr<Burger> {
switch (p) {
case Burger::BurgerType::Cheese:
return std::make_unique<CheeseBurger>();
case Burger::BurgerType::Beef:
return std::make_unique<BeefBurger>();
case Burger::BurgerType::Chicken:
return std::make_unique<ChickenBurger>();
default:
throw std::runtime_error("Unknown preference");
}
});
// "healthy" 风格工厂:Beef 被映射成 Chicken 等特殊逻辑
FunctionalBurgerFactory::registerFactory("healthy",
[](Burger::BurgerType p) -> std::unique_ptr<Burger> {
if (p == Burger::BurgerType::Beef) {
// 健康版不做牛肉,给鸡肉
return std::make_unique<ChickenBurger>();
}
// 其余偏好按原样或简化映射
return std::make_unique<CheeseBurger>();
});
// 还可以注册更多风格,比如 "luxury" 等
}
我没看到,现在的工厂返璞归真,工厂由 函数 / lambda /可调用对象 表示,而不是继承自某个抽象工厂基类。函数是可以相互组合的,我们立马发现,创建的逻辑灵活组合、替换、传递、缓存。
与之前 "嵌套工厂 + 抽象子工厂" 的对比?
特性 | 传统子工厂(抽象基类 + 派生类) | 函数式工厂(使用函数 / lambda) |
---|---|---|
类级别继承 | 需要写抽象基类、派生类 | 不需要继承,减少类数量 |
注册机制 | 通常注册派生类的构造器 / 创建函数 | 注册可调用对象(函数 / lambda) |
灵活性 | 在子类中重写行为;比较刚性 | 函数组合与替换更自由、轻量 |
额外类负担 | 类和类型较多 | 函数写在局部 / 注册代码中即可 |
可组合性 | 子工厂之间组合不方便 | 可将多个函数包装或链式组合 |
类型安全 / 静态检查 | 通过虚函数 + 类型系统 | 可能更依赖运行时检查(key 存在性等) |
工厂模式的另一侧------追踪对象的创建
我刚刚提到了,工厂模式的一个好处------忘记了?往前翻翻吧!我们下面说另一个好处。那就是,工厂模式将我们的对象一起在一个逻辑上创建了,所以,对对象的审计变得非常的容易:在 createBurgerFromPreference
中包一层追踪代码,调用实际子工厂逻辑前后插入。
cpp
#include <iostream>
#include <memory>
#include <map>
#include <mutex>
#include <chrono>
// 假设已有子工厂机制,这里略去部分代码
struct BurgerFactory {
// ... 注册子工厂机制略
static std::unique_ptr<Burger> createBurgerFromPreference(const Person& person) {
auto start = std::chrono::steady_clock::now();
std::string chosenKey = chooseFactoryKey(person);
// ... 查找子工厂并创建子工厂实例
std::unique_ptr<Burger> burger = subFactory->create(person.pref);
auto end = std::chrono::steady_clock::now();
auto dur = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
// 记录日志 / 统计
logCreation(person, chosenKey, burger.get(), dur);
return burger;
}
private:
static void logCreation(const Person& person,
const std::string& factoryKey,
Burger* burger,
long long microsec)
{
// 示例日志输出
std::cerr << "[BurgerFactory] Created burger: person.pref="
<< static_cast<int>(person.pref)
<< ", factory=" << factoryKey
<< ", ptr=" << burger
<< ", time=" << microsec << " μs\n";
// 更新统计
auto& cnt = getCounts()[burgerKey(burger)];
cnt += 1;
}
static std::map<std::string, int>& getCounts() {
static std::map<std::string, int> counts;
return counts;
}
static std::string burgerKey(Burger* b) {
// 你可以通过 RTTI(typeid)或者为每个具体类加个虚方法返回名字
return typeid(*b).name();
}
};
看到了吗?现在我们就可以批量的对对象做一定的预处理,这个内容没有其他可说。跟工厂模式远了。
所以,工厂模式和构造器模式的区别?
特性 | 工厂模式 (Factory Pattern) | 构造器模式 (Builder Pattern) |
---|---|---|
意图 | 强调 "多态创建"。根据输入条件创建出不同但接口相同的对象。 | 强调 "分步构建"。将一个复杂对象的构造过程分解,允许灵活配置。 |
对象复杂度 | 通常用于创建相对简单或结构固定的对象。 | 专门用于创建属性繁多、配置复杂的对象。 |
过程 | 一步到位。调用一个方法,直接返回所需的对象实例。 | 分步进行 。调用多个 set... 方法进行配置,最后调用 build() 方法得到对象。 |
返回值 | 工厂方法直接返回产品对象。 | set... 方法返回构造器自身 (用于链式调用),build() 方法才返回最终的产品对象。 |
接口侧重 | 侧重于一个创建方法 (create() , make() )。 |
侧重于多个配置方法 (setPartA() , setPartB() ) 和一个构建方法 (build() )。 |
-
相同点:
- 都属于创建型模式。
- 都能将对象的创建过程与客户端代码分离,降低耦合度。
- 都向客户端隐藏了具体产品的实现细节。
-
不同点与如何选择:
- 当你需要根据不同的情况(如配置文件、用户输入)创建出不同的子类型对象时,应该使用工厂模式。
- 例子 :一个UI库,需要根据操作系统是Windows还是macOS来创建不同的按钮(
WindowsButton
,MacButton
)。客户端代码不关心具体是哪种按钮,只调用通用的Button
接口的render()
方法。
- 例子 :一个UI库,需要根据操作系统是Windows还是macOS来创建不同的按钮(
- 当你需要创建一个包含大量可选参数、配置步骤繁琐的复杂对象时,应该使用构造器模式。
- 例子 :构建一个HTTP请求。这个请求可能有请求头、请求体、超时设置、认证信息等大量可选部分。使用构造器模式可以非常清晰地配置这个请求对象,
HttpRequest::Builder().setHeader(...).setBody(...).setTimeout(...).build()
。
- 例子 :构建一个HTTP请求。这个请求可能有请求头、请求体、超时设置、认证信息等大量可选部分。使用构造器模式可以非常清晰地配置这个请求对象,
- 当对象的创建过程很简单,但需要创建的对象类型不确定时,用工厂模式。
- 当要创建的对象类型是确定的,但其内部构造非常复杂、配置项繁多时,用构造器模式。
- 当你需要根据不同的情况(如配置文件、用户输入)创建出不同的子类型对象时,应该使用工厂模式。
-
可以结合使用吗?
当然可以。例如,一个抽象工厂可以不直接返回产品,而是返回一个对应产品的构造器。这样,客户端既能通过工厂解耦具体类型,又能通过构造器灵活地配置这个对象。
总结
我们在试图解决什么问题?
我们试图把 "汉堡的创建细节(把哪种子类 new 出来)" 从使用者 / 客户端剥离出来,交给一个专门的工厂体系负责。客户端只关心拿到一个 Burger
,不关心它是 CheeseBurger
还是 BeefBurger
。同时,在创建过程中我们还希望能插入追踪 / 日志 / 监控等通用行为,以及让策略 / 类别的扩展变得容易、开闭。展开的说:
- 客户端(
enjoy_our_meals
/Person::enjoy_burger
)只关心 "得到一个可以吃的汉堡(Burger 接口)",而不是关心汉堡是怎样被构造的。 - 汉堡有多种具体类型(Cheese、Beef、Chicken......将来可能更多)。
- 汉堡的创建细节(哪种汉堡类、哪个构造函数、可能的初始化参数等)不应暴露在客户端。
- 如果未来要加一种新汉堡类型(比如 "FishBurger" 或 "VeganBurger"),希望最少改动客户端 / 高层逻辑。
- 另外,可能还要插入一些通用逻辑(例如创建时记录日志 / 监控 / 统计 / 安全检查等) ------这不是汉堡本身的职责,而是工厂层的职责。
- 最后,可能会有多个不同策略去造汉堡(例如健康版、豪华版、优惠版等),我们希望策略之间易于切换、组合、替换。
事情说开了:
痛点 / 目标:
- 解耦 :客户端不依赖具体汉堡类,只依赖
Burger
抽象接口。- 隐藏构造逻辑 :避免客户端写
switch
/if
/new
这些细节。- 可扩展 / 开闭:加新汉堡类型 / 策略时,不要改客户端 / 主要流程。
- 集中控制 / 可插入额外处理:在汉堡创建流程中插日志、统计、权限校验等,而不散落到各处。
- 策略切换:可以在不同情境(顾客偏好、时间、配置、促销、健康要求等)下切换哪种"风格"来造汉堡。
因此,我们希望用一种设计 ------工厂模式及其变体 ------把 "汉堡的创建过程" 抽象 / 封装 / 统一管理起来。
我们怎么解决这个问题?
- 把产品接口与具体实现分离
定义Burger
(纯虚基类 / 接口),并让具体的CheeseBurger
、BeefBurger
、ChickenBurger
等子类实现它。客户端只用Burger*
或std::unique_ptr<Burger>
来处理。 - 把创建逻辑从客户端剥离出来,放到工厂里
客户端不要new CheeseBurger
、new BeefBurger
、或写switch
判断;而是调用BurgerFactory::createBurgerFromPreference(person)
。工厂负责根据偏好 / 策略选择出具体子类。 - 如果策略越来越复杂 / 产品类别增多 → 用多级 / 嵌套 / 抽象工厂结构
顶层工厂不直接造汉堡,而是根据策略 / 配置 /偏好选择子工厂(或策略类)来造。子工厂专注于某类产品或某种策略(健康汉堡、豪华汉堡等)。 - 可能用"函数式工厂"(函数 / λ表达式)来取代 / 简化类层次
不一定每个子工厂都写成一个类,可以把创建逻辑写成函数 / lambda 注册到一个映射表里,用 key → 创建函数的方式实现策略切换。 - 在工厂层插入"追踪 / 日志 / 监控 / 统计"
既然创建都集中在工厂里,那么工厂是最天然的切点,可以在每次创建前 / 后插入记录、计数或通知的机制。这样,对象的追踪变得非常的容易!
各个工厂模式 / 变体的方案 + 优缺点
下面是几种常见的工厂变体 / 模式,在你汉堡场景中的作用、实现方式、优点和缺点对比。
模式 / 变体 | 实现方式/结构 | 在汉堡场景中的作用 / 举例 | 优点 | 缺点 / 需要付出的代价 |
---|---|---|---|---|
最简单工厂 (Simple / Static Factory 函数 / 方法) | 用一个函数或静态方法(如 BurgerFactory::createBurgerFromPreference )包含一个 switch(person.pref) 或 if 逻辑来决定 new CheeseBurger 、new BeefBurger 等 |
最开始你写的那个版本就是这个:客户端调用 createBurgerFromPreference(each_person) 得到一个 Burger* |
实现简单、直接;客户端和创建逻辑解耦;添加新汉堡类型只改工厂函数中的 switch |
工厂函数可能越来越臃肿;switch / if 扩展时风险高;当策略复杂时工厂函数压力大 |
工厂类 / 静态工厂方法 (Factory Class + 方法封装) | 把那个静态函数封装成 struct BurgerFactory ,客户端调用 BurgerFactory::createBurgerFromPreference |
如你示例的 struct BurgerFactory { static Burger* createBurgerFromPreference(...) } |
分离职责、可以控制访问、可以插日志 / 控制访问 | 仍然是单一位置处理全部逻辑;如果产品种类 / 策略很多,工厂类可能变复杂 |
工厂方法模式 (Factory Method pattern) | 定义一个抽象 Creator / Factory 类,它有一个 create() 抽象方法。不同子类实现 create() ,返回不同的具体产品。客户端通过某个子类工厂去创建 |
例如定义 BurgerCreator 抽象基类,有子类 CheeseBurgerCreator 、BeefBurgerCreator 、ChickenBurgerCreator ,它们分别 create() 出对应的汉堡 |
符合开闭原则:要新增产品只新增一个子类工厂;客户端和具体类型解耦;可以在子工厂里插入特殊逻辑 | 工厂类 / 子类较多,类层次多;切换策略要知道用哪个 Creator 子类;可能代码组织变得复杂 (refactoring.guru) |
抽象工厂模式 (Abstract Factory) | 当你不仅创建汉堡(Burger),还要创建和它相关的其他产品(例如饮料 Drink 、配菜 SideDish 等)时,用抽象工厂来定义一组创建接口(createBurger() 、createDrink() ...)。子工厂实现一个完整产品族 |
例:HealthyFoodFactory 可以创建健康汉堡 + 无糖饮料 + 蔬菜沙拉组合;LuxuryFoodFactory 创建豪华汉堡 + 高级饮料 + 甜点 |
可以一次返回一组 "产品族" 的对象,确保这些对象在同一风格 / 一致性;客户端只与抽象接口耦合,无需知道具体类 | 抽象工厂的类层次复杂;如果产品族或者组合很多,維護量大 (refactoring.guru) |
嵌套工厂 / 工厂 of 工厂 (Factory of Factories / Top-level 调度 + 子工厂体系) | 顶层工厂(入口)负责调度 / 选择哪一个子工厂(或策略工厂)来创建产品;子工厂才是真正造具体汉堡 | 你之前问的嵌套工厂例子:BurgerFactory 顶层有注册表 / 映射表,选一个 ConcreteBurgerFactory 子工厂来造具体汉堡 |
顶层逻辑和具体造汉堡逻辑分离;添加新策略 / 子工厂不改顶层;策略切换容易;结构清晰 | 要维护注册机制 / 映射表;稍微复杂点;如果层次太深可能理解成本上升 |
函数式工厂 (Functional Factory / 工厂用函数 / lambda) | 不用写工厂类继承层次,而把创建逻辑写成函数 / lambda(或 std::function ),注册成 key → 创建函数映射 |
例如注册 "simple" → [](...) { switch → new Cheese/Beef/Chicken } ;客户端调用 FunctionalBurgerFactory::createBurger(key, pref) |
灵活、代码少、组合性强;无需为每个策略写类;注册 / 切换简单 | 过度依赖运行时代码(例如查表、错误处理);类型安全弱一点;大型策略逻辑可能变成难调试的 lambda |
装饰 / 代理 / Logging 工厂 | 不改原始工厂逻辑,而在工厂外层用装饰器 / 代理模式包一层 "LoggingFactory" 或 "TracingFactoryDecorator",在调用真正工厂前后插入日志 / 监控 | 例如把 SimpleBurgerFactory 包在 TracingFactoryDecorator<SimpleBurgerFactory> 中,让它在 create(...) 前后记录时间、统计次数 |
追踪逻辑和创建逻辑分离;可选装饰 / 可动态开启 / 关闭;不会污染产品类 | 增加一层间接;理解稍复杂;要处理好代理层的透明性(例如参数传递、异常、所有权) |
钩子 / 观察者机制 + 工厂 | 工厂内部定义回调 / 观察者接口(onBeforeCreate 、onAfterCreate 等),允许外部注册观察者来监听 "创建事件" |
在 BurgerFactory::createBurgerFromPreference 或子工厂内部在创建前 / 后触发回调,外部日志 / 指标系统注册回调 |
灵活、可插拔;追踪逻辑与核心逻辑解耦;支持多个监听者 | 增加回调管理复杂性;要处理线程安全、回调异常;可能有性能开销 |
- 简单工厂 / 静态工厂方法:适合简单情况,低成本,但扩展性有限。
- 工厂方法(Factory Method):把创建责任交给子类工厂,增加了灵活性和扩展性,但类层次增多。
- 抽象工厂(Abstract Factory):适合产品族组合,用于多个相关产品并保持一致风格。
- 嵌套工厂 / 工厂 of 工厂:顶层调度 + 子工厂体系,适合策略 / 子集复杂的情形。
- 函数式工厂:用函数 / lambda 实现工厂逻辑,简洁灵活、少类,但静态类型/安全性稍弱。
- 装饰 / 代理 / 钩子机制:用于在工厂体系中插追踪 / 日志 / 监控,不影响核心创建逻辑。