第9章:抽象工厂
对齐第一版第9章 Abstract Factory:产品族的架构角色、Typelist 生成的通用接口、ConcreteFactory 类层次实现、Prototype-based 实现与完整族一致性契约。
学习目标
- 能解释 Abstract Factory 的 architectural role,并区分单产品 Object Factory 与整族产品一致性选择
- 能实现 ProductList 驱动的 generic Abstract Factory interface 与 ConcreteFactory class generation,推导每个 Unit 的职责
- 能设计 prototype-based Abstract Factory,处理 prototype ownership、缺失产品、clone semantics 与原子替换
机制总览
第9章:抽象工厂:机制路径
- 1
为什么选择单位是“一族产品”
The Architectural Role of Abstract Factory 是隔离一整组相互兼容 products 的创建。UI toolkit 需要同一平台的 Button/Menu/Dialog;数据库 adapter 需要同一 driver 的 Connection/Command/…
- 2
A Generic Abstract Factory In…
传统 interface 手写 MakeButton/MakeMenu/MakeDialog virtual methods;product 集合变化时,abstract 与每个 concrete factory 都要同步修改。
- 3
Implementing AbstractFactory
Implementing AbstractFactory 需要把 abstract product list 与 concrete product list 对齐。
章级决策实验
第9章:抽象工厂:机制与证据
切换《第9章:抽象工厂》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。
选择推理阶段
当前阶段 · 为什么选择单位是“一族产品”
The Architectural Role of Abstract Factory 是隔离一整组相互兼容 products 的创建。UI toolkit 需要同一平台的 Button/Menu/Dialog;数据库 adapter 需要同一 driver 的 Connection/Command/…
可核验证据
用正向与应拒绝的编译案例、生成类型和生命周期测试核对「为什么选择单位是“一族产品”」的组合规则与扩展边界。
学完《第9章:抽象工厂》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。
失效—证据矩阵
第9章:抽象工厂:失效与核验
为什么选择单位是“一族产品”
典型失效
若只复制「为什么选择单位是“一族产品”」模板结构而不声明替换点、所有权和实例化边界,组合后的类型会迅速产生二义性或不可诊断错误。
核验证据
用正向与应拒绝的编译案例、生成类型和生命周期测试核对「为什么选择单位是“一族产品”」的组合规则与扩展边界。
A Generic Abstract Factory In…
典型失效
若只复制「A Generic Abstract Factory In…」模板结构而不声明替换点、所有权和实例化边界,组合后的类型会迅速产生二义性或不可诊断错误。
核验证据
用正向与应拒绝的编译案例、生成类型和生命周期测试核对「A Generic Abstract Factory In…」的组合规则与扩展边界。
Implementing AbstractFactory
典型失效
若只复制「Implementing AbstractFactory」模板结构而不声明替换点、所有权和实例化边界,组合后的类型会迅速产生二义性或不可诊断错误。
核验证据
用正向与应拒绝的编译案例、生成类型和生命周期测试核对「Implementing AbstractFactory」的组合规则与扩展边界。
为什么选择单位是“一族产品”
The Architectural Role of Abstract Factory 是隔离一整组相互兼容 products 的创建。UI toolkit 需要同一平台的 Button/Menu/Dialog;数据库 adapter 需要同一 driver 的 Connection/Command/Transaction。client 只依赖 abstract interfaces,并一次选择完整 family。
↡提供一组相关或相容抽象产品的创建接口,使 client 不依赖 concrete classes 且保持产品族一致。若 client 分别从三个 Object Factories 选择产品,可能得到 DesktopButton + TouchMenu + FakeDialog;每个对象单独合法,组合 invariant 却破坏。Abstract Factory 把 family selection 提升为一个 object/type,从 architecture 上关闭混搭路径。
A Generic Abstract Factory Interface
传统 interface 手写 MakeButton/MakeMenu/MakeDialog virtual methods;product 集合变化时,abstract 与每个 concrete factory 都要同步修改。A Generic Abstract Factory Interface 把 abstract products 放入 Typelist,让 class generation 为每个 T 生成一个 AbstractFactoryUnit<T>。
template<class T>
struct AbstractFactoryUnit {
virtual std::unique_ptr<T> create(Type2Type<T>) = 0;
virtual ~AbstractFactoryUnit() = default;
};
template<class ProductList>
class AbstractFactory
: public GenScatterHierarchy<ProductList, AbstractFactoryUnit> {
public:
template<class T>
std::unique_ptr<T> create() {
auto& unit = static_cast<AbstractFactoryUnit<T>&>(*this);
return unit.create(Type2Type<T>{});
}
};scatter hierarchy 让 factory 同时拥有每个 product 的 virtual creator unit。Type2Type<T> 解决函数不能仅按 return type overload;client 调 factory.create<Button>(),template front-end 下钻到对应 virtual unit。
product list 是 schema:duplicate 会生成 ambiguous base,缺失会让 client 无 slot,顺序可能影响 paired concrete list 映射。应在 compile time 检查 unique、length 与 pairwise inheritance。
Implementing AbstractFactory
Implementing AbstractFactory 需要把 abstract product list 与 concrete product list 对齐。ConcreteFactory<AbstractFactory, CreatorUnit, ConcreteList> 通过 linear hierarchy 逐层实现一个 creation slot,并沿 Tail 继续。
template<class ConcreteProduct, class Base>
class OpNewFactoryUnit : public Base {
using AbstractProduct = typename Base::Product;
public:
std::unique_ptr<AbstractProduct>
create(Type2Type<AbstractProduct>) override {
static_assert(std::is_base_of_v<AbstractProduct, ConcreteProduct>);
return std::make_unique<ConcreteProduct>();
}
};
using TouchFactory = ConcreteFactory<
UiAbstractFactory,
OpNewFactoryUnit,
TypeList<TouchButton, TouchMenu, TouchDialog>>;真正实现需要在 hierarchy recursion 中让每一层知道对应 abstract head;上面片段只突出 pairwise contract。length 相同不够,每个 Concrete_i 都必须公开派生自 Abstract_i,constructor signature 与 owner/deleter 也要兼容。
为什么一边 scatter、一边 linear
abstract interface 需要并列的 virtual slots,scatter hierarchy 合适;concrete implementation 需要逐项消费 concrete list 并层层 override,linear hierarchy 合适。Typelist 在这里不是“炫技容器”,而是同一 family schema 同时驱动接口和实现。
↡让同一 ProductList 同时生成并列 abstract slots 与顺序 concrete overrides,避免平行声明漂移的结构。error diagnostics 是成本:列表第 17 项错配会产生深层 base error。modern concepts 可在最外层验证 list sizes/pairs,并用 index/type 名报告;对于只有三种产品的固定系统,手写 virtual interface 可能更可维护。
A Prototype-Based Abstract Factory Implementation
A Prototype-Based Abstract Factory Implementation 不把 concrete product types 固定在 factory type 中,而为每种 abstract product 保存一个 prototype。Create<T> 找到对应 slot 并调用 virtual clone,运行时替换 prototypes 就能改变 family realization。
class PrototypeUiFactory : public UiAbstractFactory {
public:
void setButton(std::unique_ptr<Button> value) {
button_ = std::move(value);
}
std::unique_ptr<Button> create(Type2Type<Button>) override {
if (!button_) throw MissingPrototype{"Button"};
return button_->clone();
}
private:
std::unique_ptr<Button> button_;
};这个接口对每类产品仍是手写,generic implementation 可用 product list 生成 prototype slots。关键差异是 concrete state 来自 objects,不是 template types;因此同一 factory class 能在 runtime 切换 theme/config。
prototype ownership 可 unique/shared/non-owning,但 clone 期间必须 live。替换单个 prototype 可能暂时混合两族;如果 family invariant 必须强一致,应构造完整 immutable prototype set,再 atomic swap factory snapshot,而不是逐 slot 更新。
↡将完整 prototype family 视为不可变快照并一次发布,避免并发 client 观察到半更新产品族。AbstractFactory and ConcreteFactory Quick Facts
AbstractFactory and ConcreteFactory Quick Facts 可以整理为:
- abstract product list 定义 family schema;Factory interface 为每个 product 提供 creation slot。
- concrete list 必须等长且逐项满足 base relation;CreatorUnit 决定 new、prototype 或 custom construction。
- client 选择 factory,不选择 concrete products;family consistency 是 pattern 的主要收益。
- template-generated hierarchy 适合产品多、family 多且结构稳定;产品少时手写接口通常更清楚。
与 Object Factory 的组合
Object Factory 可以按 runtime familyId 返回一个 unique_ptr<AbstractFactory>,之后 client 用它创建整族产品。第一层解决“选哪个 family”,第二层解决“在这个 family 中创建哪些 products”。不要把所有 product ID 和 family ID 压成一个二维 string registry,否则一致性又回到 caller 手中。
先预测:若新加一个 abstract product,所有 concrete families 都必须提供实现。这是 Abstract Factory 的强项也是成本:它确保 family completeness,却让 product dimension 扩展昂贵。若系统频繁加 product kinds、很少加 families,Visitor/registry 等其他结构可能更合适。
第一步:证明 family invariant
列出 products 之间共享的平台、协议、version 或 test contract;若允许自由混搭,就不需要 Abstract Factory。
小结
- Abstract Factory 的 architecture role 是一次选择相容 product family,而不是独立选择多个 concrete objects
- generic interface 由 ProductList + scatter hierarchy 为每种 abstract product 生成 virtual slot
- ConcreteFactory 用 concrete list 与 CreatorUnit 生成 overrides,必须逐项验证对应与 inheritance
- prototype-based implementation 把 concrete choice/state 放到 runtime objects,适合动态配置 family
- prototype slots 单独正确不保证 family 一致;完整 snapshot 与原子发布可避免半更新
- product dimension 新增会影响所有 families,family dimension 新增较便宜;pattern 选择要匹配扩展方向
- Object Factory 可选择 AbstractFactory,后者再创建 family products,两层职责不应混成平面 registry
练习
- 问题 1:判断是否需要 Abstract Factory。 Renderer 有 Vulkan/OpenGL families,各自创建 Buffer/Texture/Pipeline,资源不能跨 backend 混用;说明 family schema 与 client contract。
- 问题 2:验证 concrete list。 Abstract list 为 Button/Menu/Dialog,Concrete list 为 TouchButton/DesktopMenu/TouchDialog;编译上都各自派生正确,为什么仍应拒绝?
- 问题 3:原子切换 prototype theme。 多线程 client 创建 UI products,同时更新三个 prototypes;设计一致 snapshot。
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- Abstract Factory
- 创建一组相容抽象产品并隐藏具体 classes 的组件。
- generic Abstract Factory interface
- 由产品类型列表生成的每产品创建协议。
- AbstractFactoryUnit
- 为单个 abstract product 定义 virtual creation slot 的生成单元。
- ConcreteFactory
- 把 concrete product list 映射到 abstract slots 的生成实现。
- family class generation
- 同一 schema 驱动 abstract scatter 与 concrete linear hierarchy 的结构。
- Prototype Abstract Factory
- 保存每产品 prototype 并 clone 新对象的 runtime-configurable 实现。
- family snapshot
- 一次拥有、验证并原子发布完整 prototype family 的不可变状态。
- Abstract Factory contract
- schema、concrete mapping、creator、owner 与 family invariant 的整体约束。