← 返回题目列表

参数化工厂、注册表工厂和工厂方法有什么关系?

高频 困难 第 13 / 25 题 更新于 2026/07/28
工厂方法模式注册表工厂参数化工厂策略模式

简化版

参数化工厂用参数决定创建哪种产品,注册表工厂把参数和具体工厂或构造函数映射起来;它们常用于解决工厂选择问题。严格说它们不一定是标准工厂方法,但可以和工厂方法结合,让新增产品通过注册扩展而不是修改大分支。

详细版

参数化工厂常见写法:

Parser create(String type) {
    if ("json".equals(type)) return new JsonParser();
    if ("xml".equals(type)) return new XmlParser();
    throw new IllegalArgumentException(type);
}

注册表工厂会把分支变成映射:

class ParserRegistry {
    private final Map<String, ParserFactory> factories = new HashMap<>();

    Parser create(String type) {
        ParserFactory factory = factories.get(type);
        if (factory == null) throw new IllegalArgumentException(type);
        return factory.create();
    }
}

这里 ParserFactory 可以是工厂方法接口。新增产品时,新增具体工厂并注册进去,中心创建逻辑不再随着产品数量线性膨胀。

这种设计常见于插件系统、消息处理器、支付渠道、文件解析器。要注意注册表只解决“如何找到工厂”,工厂方法解决“如何创建产品”,二者关注点不同。

完整版教学

一、参数化工厂解决选择入口

业务通常需要根据类型、协议、配置或租户选择产品。参数化工厂把选择入口统一到一个方法里,调用方不用自己写分支。

它的问题是产品越多,中心方法越大;每次新增产品都修改同一个类,也容易出现冲突。

二、注册表工厂把分支改成数据结构

注册表通过 Map<key, factory> 管理创建能力:

interface ParserFactory {
    Parser create();
}

registry.register("json", new JsonParserFactory());
registry.register("xml", new XmlParserFactory());

创建时只负责查表和调用工厂,不关心每个产品的构造细节。

三、它和策略模式容易一起出现

如果创建出来的产品是可替换算法或处理器,注册表里存的可能是策略对象,也可能是创建策略对象的工厂。

区别在于:策略模式关注“运行时执行哪种行为”,工厂方法关注“如何创建对象”。支付渠道场景里,PayHandler 是策略,PayHandlerFactory 是创建策略的工厂。

四、注册方式影响扩展性

注册可以是手动代码注册、配置文件注册、Spring 注入 Map、SPI 扫描或插件加载。注册越自动,扩展越灵活,但排查问题也可能更复杂。

面试回答可以补充工程取舍:小系统用显式注册更清楚,大型插件化系统才需要扫描和动态加载。

五、把选择、查找和创建拆成三段

参数化工厂把 key 送进中心分支;注册表把 key 到创建能力的关系变成数据;具体工厂再负责构造产品。三者可以叠加,但它们解决的是三段不同问题,不能用“都有 create 方法”抹平差异。

调用方提供 key="json"
ParserRegistry.get("json")
得到 JsonParserFactory
factory.create(options)
返回 Parser 抽象
Parser.parse(document)
新增 yaml = 新工厂 + register("yaml", factory)

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

六、模式边界与扩展成本

维度本题结论
稳定抽象Map<Key, ParserFactory> 的查找规则和 ParserFactory.create 创建契约。
主要变化点key 集合、工厂注册来源与具体产品构造。
新增一种产品的动作新增工厂并注册;是否零修改取决于注册是在代码、配置还是插件发现中。
不应放进工厂的职责把策略执行、权限判断和复杂业务路由全部塞进注册表。
更轻或更合适的方案少量稳定类型使用枚举 switch;无状态产品可直接注册产品/策略实例。

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

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

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

记忆钩子:key 负责选,registry 负责找,factory 负责造,product 负责做。

八、常见误区与追问

  • 误区:注册表天然符合开闭原则。 若每次仍修改中心静态初始化代码,只是把分支换成映射。
  • 误区:注册表和策略模式是同一个模式。 注册表负责定位对象,策略负责可替换行为。
  • 误区:动态注册只需换成 ConcurrentHashMap。 还要定义覆盖、撤销、可见性与产品生命周期。
  • 追问:Map 中应存 Supplier 还是 Factory? 无参数创建可用 Supplier;需要清晰配置契约时专用 Factory 更表达语义。
  • 追问:未知 key 如何处理? 使用明确异常、Optional 或业务定义的默认项,避免返回 null。
  • 追问:插件卸载时怎么办? 应移除注册、停止新建产品并关闭已创建资源,不能只删一个 Map 项。

九、加强记忆

参数化工厂回答“根据什么 key 选择”,注册表工厂回答“key 和工厂怎么映射”,工厂方法回答“具体对象怎么创建”。三者可以组合,但不要把它们混成同一个概念。