← 返回题目列表

工厂方法和抽象工厂有什么区别?

高频 中等 第 3 / 25 题 更新于 2026/07/28
工厂方法模式抽象工厂模式创建型模式产品族

简化版

工厂方法通常关注创建一种产品,由具体工厂决定返回哪个具体产品;抽象工厂关注创建一组相关产品,也就是产品族。工厂方法是一条产品线的扩展,抽象工厂是多条相关产品线的一致创建。

详细版

区别可以这样记:

维度工厂方法抽象工厂
创建对象一类产品一组相关产品
工厂接口通常一个创建方法多个创建方法
变化重点新增同一等级的产品切换整套产品族
例子ParserFactory.createParser()UIFactory.createButton() + createCheckbox()

工厂方法例子:

interface ParserFactory {
    Parser createParser();
}

抽象工厂例子:

interface UIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

如果只需要创建 Parser 一种产品,工厂方法够用;如果要保证 Windows 按钮和 Windows 复选框成套出现,Mac 按钮和 Mac 复选框成套出现,就更适合抽象工厂。

完整版教学

一、先理解产品等级和产品族

产品等级指同一类产品的不同实现,例如 Button 有 WindowsButton、MacButton。产品族指属于同一风格或平台的一组产品,例如 Windows 风格下的 Button、Checkbox、Menu。

工厂方法主要处理产品等级扩展,抽象工厂主要处理产品族一致性。

二、工厂方法适合一类产品

当系统只关心创建某一类对象时,工厂方法就足够。比如不同格式的解析器都实现 Parser,新增 CSV 解析器时新增 CsvParserFactory

这时引入抽象工厂会显得多余,因为没有一组相关产品需要同时创建。

三、抽象工厂适合成套产品

如果业务要求一组对象必须来自同一风格、协议或平台,抽象工厂更合适。比如 UI 组件、数据库方言对象、云厂商 SDK 适配对象。

interface CloudFactory {
    StorageClient createStorage();
    QueueClient createQueue();
}

阿里云工厂创建阿里云的一组客户端,AWS 工厂创建 AWS 的一组客户端,调用方不用担心混用。

四、两者可以组合使用

抽象工厂中的每个创建方法本身也可以看作工厂方法。也就是说,抽象工厂不是工厂方法的反面,而是在更高维度组织多个工厂方法。

面试里可以用这句话收束:工厂方法解决“一个产品怎么扩展”,抽象工厂解决“一组产品怎么保持一致”。

五、用二维产品矩阵决定需要一条创建线还是一整族

只有 JSON、XML、CSV 三种 Parser 时,产品只有一个等级,工厂方法足够;若每个平台都要成套创建 Parser、Validator、Serializer,且同平台对象不能混搭,才进入抽象工厂的产品族问题。

单等级: Parser <- JsonParser / XmlParser
工厂方法: JsonParserFactory.createParser()
多等级: Parser + Validator + Serializer
产品族 A: AParser + AValidator + ASerializer
产品族 B: BParser + BValidator + BSerializer
抽象工厂: createParser/createValidator/createSerializer
客户端选择一族工厂后成套创建

这段协作图要回答两个不同问题:产品由谁构造,具体工厂又由谁选择。工厂方法可以把产品构造从业务流程中移走,但应用入口、配置层或容器仍然必须决定使用哪个工厂;声称模式“消灭了所有分支”并不准确。

六、模式边界与扩展成本

维度本题结论
稳定抽象工厂方法稳定一个产品接口;抽象工厂稳定多个相关产品接口。
主要变化点前者主要变化具体产品,后者主要变化整套产品族。
新增一种产品的动作单产品新增具体工厂;产品族新增一个实现所有创建方法的具体抽象工厂。
不应放进工厂的职责把没有兼容关系的产品硬塞成一族。
更轻或更合适的方案只有一类产品用工厂方法;相关产品可独立选择时用多个独立工厂。

是否符合开闭原则不能只数新增了多少类,而要看高风险旧代码是否仍被反复修改。若每次扩展仍要修改一个中心 switch、产品接口又泄漏具体类型,即使类名叫 Factory,也没有获得工厂方法的主要收益。

七、用三个测试验证设计没有走样

  1. 契约测试:对每个具体工厂执行同一组产品接口测试,确认返回值满足抽象契约。
  2. 扩展测试:加入一个假产品和假工厂,记录为了接入它修改了哪些旧文件。
  3. 未知类型测试:若存在注册表或参数路由,明确缺失 key 的异常类型和降级策略。
  4. 生命周期测试:连续创建两次,确认产品应当复用还是隔离,避免无意变成单例或重复昂贵连接。
  5. 依赖测试:具体构造参数应由工厂显式获得,业务类不应通过全局容器偷偷查找。
  6. 并发测试:注册表若支持运行期写入,必须定义发布、覆盖和读取的一致性;只读注册表则尽量启动期冻结。
  7. 可观测性测试:记录命中的工厂 key 与具体实现,排查配置选择错误时无需猜测。

记忆钩子:一条产品等级线用工厂方法,必须成套切换的多条线用抽象工厂。

八、常见误区与追问

  • 误区:抽象工厂只是返回类型写成接口的工厂方法。 它的辨识点是创建一组相关产品并维持产品族一致性。
  • 误区:工厂方法只能创建一个对象实例。 它可被调用多次;“一种产品”指产品等级,不是数量。
  • 误区:两个模式不能组合。 抽象工厂中的每个创建方法本身就具有工厂方法性质。
  • 追问:新增产品等级谁更痛苦? 抽象工厂需修改接口和所有具体工厂,影响面更大。
  • 追问:新增产品族谁更自然? 抽象工厂新增一个具体工厂及整套具体产品即可。
  • 追问:如何避免抽象工厂接口膨胀? 按稳定产品族边界拆分,不相关或频繁变化的等级使用独立工厂。

九、加强记忆

看到“创建一种产品”,优先想到工厂方法;看到“一整套相关产品不能混搭”,优先想到抽象工厂。前者是一条线,后者是一整组线。