抽象工厂中的产品族和产品等级是什么意思?
简化版
产品等级表示同一类产品的不同实现,例如 Button 有 WindowsButton 和 MacButton;产品族表示同一风格或平台下的一组相关产品,例如 WindowsButton、WindowsCheckbox、WindowsMenu。理解这两个概念,是区分抽象工厂和工厂方法的关键。
详细版
可以用一个二维表理解:
| Button 产品等级 | Checkbox 产品等级 | |
|---|---|---|
| Windows 产品族 | WindowsButton | WindowsCheckbox |
| Mac 产品族 | MacButton | MacCheckbox |
纵向看是一族:Windows 产品族包含 WindowsButton、WindowsCheckbox。横向看是一类产品等级:Button 产品等级包含 WindowsButton、MacButton。
抽象工厂通常按产品族创建对象:
interface UIFactory {
Button createButton();
Checkbox createCheckbox();
}
WindowsFactory 创建 Windows 族的所有产品,MacFactory 创建 Mac 族的所有产品。这样调用方切换工厂时,就能整体切换一套产品。
完整版教学
一、产品等级是同一种产品的不同实现
产品等级关注“它们是不是同一类东西”。例如所有按钮都实现 Button 接口,所有复选框都实现 Checkbox 接口。
如果只是在 Button 这一条线上扩展 WindowsButton、MacButton、LinuxButton,更像是工厂方法要处理的问题。
二、产品族是同一套风格或上下文
产品族关注“它们是不是应该成套出现”。WindowsButton 和 WindowsCheckbox 不是同一类产品,但它们属于同一套 Windows 风格。
抽象工厂就是为了保证这些相关产品由同一个工厂产出,从而保持风格、协议或配置一致。
三、二维视角能看清扩展方向
抽象工厂的扩展有两个方向:
- 新增产品族:新增一行,例如 LinuxFactory;
- 新增产品等级:新增一列,例如 Menu。
新增产品族通常比较顺滑;新增产品等级会影响抽象工厂接口和所有具体工厂。
四、面试中常用例子
跨平台 UI 是最经典的例子,除此之外还可以举:
- 数据库方言:MySQLSqlBuilder、MySQLTypeMapper 和 PostgreSQLSqlBuilder、PostgreSQLTypeMapper;
- 云厂商 SDK:AliStorage、AliQueue 和 AwsStorage、AwsQueue;
- 主题皮肤:DarkButton、DarkDialog 和 LightButton、LightDialog。
五、横看产品等级,竖看产品族
二维表的方向必须说准确:同一列是同类产品的不同实现,即产品等级;同一行是同一平台/主题下的不同产品,即产品族。借助接口和工厂方法可把两个维度落实到代码。
Button产品等级 Checkbox产品等级 Menu产品等级
Windows族 WinButton WinCheckbox WinMenu
Mac族 MacButton MacCheckbox MacMenu
同列产品实现同一抽象接口
同行产品由同一具体工厂创建
加平台 = 加行
加组件类别 = 加列
矩阵的每一行是一族兼容产品,每一列是一个产品等级。客户端选择一行对应的具体工厂,再从该工厂取得各列产品;如果允许调用方绕过工厂分别选择每个具体类,产品族一致性的结构性保证就消失了。
六、二维扩展成本与模式边界
| 评审维度 | 本题结论 |
|---|---|
| 产品族不变量 | 具体工厂对应一行,返回的每一列产品都属于该行产品族。 |
| 新增一行(产品族) | 增加一行工厂及各列具体产品。 |
| 新增一列(产品等级) | 增加一列抽象产品、工厂方法和每行实现。 |
| 不应由工厂承担 | 把“产品族”误说成同一接口下的多个实现。 |
| 退回更轻方案的条件 | 没有成套关系时分别使用工厂方法或直接注入产品。 |
抽象工厂对所有变化并非都开放。它刻意固定列集合,以换取整行切换的一致性;如果系统最常发生的是新增列,工厂接口和所有行实现会频繁联动,此时应拆分工厂边界或选择组合方式。
七、落地验收清单
- 为每个具体工厂跑同一组契约测试,确认每个创建方法都返回正确产品等级。
- 给产品暴露
familyId()仅用于测试,校验同一工厂创建的所有产品 familyId 一致。 - 加入一个假产品族,记录是否只新增一行实现和装配配置,而未修改旧工厂。
- 模拟新增一个产品等级,评估抽象接口和全部具体工厂的真实修改成本。
- 检查公共产品接口是否出现 AWS、MySQL、Windows 等具体实现词汇。
- 检查具体工厂是否只做组装与创建,没有吞入完整业务流程。
- 若产品持有连接或线程池,明确由工厂、容器还是调用方负责关闭。
- 若运行期按租户切换工厂,验证路由缓存、凭证刷新和租户隔离。
记忆钩子:列回答“这是什么产品”,行回答“它属于哪一套”。
八、常见误区与追问
- 误区:WindowsButton 和 MacButton 属于同一产品族。 它们同属 Button 产品等级,但分属 Windows、Mac 两族。
- 误区:WindowsButton 和 WindowsCheckbox 属于同一产品等级。 它们产品类型不同,只是同属 Windows 产品族。
- 误区:产品族只能表示 UI 风格。 数据库、云厂商、协议栈等兼容套件同样适用。
- 追问:新增主题皮肤属于哪一维? 若提供全部既有组件实现,就是新增产品族/一行。
- 追问:新增 Slider 属于哪一维? 它是新产品等级/一列,会影响所有具体工厂。
- 追问:这个概念为何重要? 它直接决定抽象工厂适用性和两个扩展方向的成本。
九、加强记忆
产品等级看“同类产品”,产品族看“成套产品”。抽象工厂最关心的是产品族,工厂方法更常处理某一个产品等级下的扩展。