← 返回题目列表

什么是简单工厂和工厂方法模式?

高频 简单 第 2 / 25 题 更新于 2026/07/28
工厂方法模式简单工厂创建型模式对象创建

简化版

简单工厂把对象创建集中到一个工厂类里,由参数决定创建哪种产品;工厂方法把创建逻辑下放到具体工厂子类,让每个工厂负责一种产品。两者都在隐藏 new 的细节,但工厂方法更符合开闭原则。

详细版

简单工厂通常长这样:

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

调用方只依赖 ParserFactory.create(type)Parser 接口,不需要到处写 new JsonParser()。它的缺点是新增产品时要修改工厂类,分支越来越多。

工厂方法会把“创建哪种产品”交给具体工厂:

interface ParserFactory {
    Parser create();
}

class JsonParserFactory implements ParserFactory {
    public Parser create() {
        return new JsonParser();
    }
}

新增 YamlParser 时新增 YamlParserFactory,原有工厂可以不改。面试里要强调:简单工厂不是 GoF 23 种设计模式之一,更像一种常用创建封装;工厂方法才是经典创建型设计模式。

完整版教学

一、它们共同解决什么问题

业务代码里如果到处直接 new 具体类,会出现两个问题:第一,调用方知道太多具体类型;第二,创建规则变化时要改很多地方。工厂类把创建逻辑集中起来,让调用方依赖稳定的接口。

例如报文解析场景里,业务层真正关心的是“拿到一个能解析报文的 Parser”,而不是 JsonParser 的构造参数、默认配置和初始化顺序。工厂就是把这些创建细节藏起来。

二、简单工厂的特点

简单工厂一般由一个静态方法或普通方法负责创建产品,输入通常是类型、枚举或配置。它简单、直接,适合产品种类少、变化不频繁的场景。

它的代价也明显:每新增一种产品,都要修改同一个工厂方法。如果产品越来越多,工厂会变成一大段条件分支,测试和维护都会变重。

三、工厂方法的特点

工厂方法把“创建产品”定义成一个抽象方法,让具体工厂去实现。调用方可以依赖抽象工厂,也可以通过配置、容器或注册表选择具体工厂。

这种方式的优势是扩展新产品时新增类,而不是修改旧分支。缺点是类的数量会增加,如果产品变化很少,直接上工厂方法可能显得偏重。

四、面试回答要主动区分边界

回答时不要把简单工厂和工厂方法混成一个东西。可以这样表达:简单工厂侧重集中创建,牺牲了一部分开闭原则;工厂方法侧重通过多态扩展创建逻辑,让产品创建这件事也能被替换。

还要指出工厂方法并没有让具体工厂自动出现,系统组合根仍需根据配置选择或注册它。这样既讲清了多态扩展的收益,也没有夸大成“新增产品完全不修改任何地方”。

五、用解析器例子把两种创建结构完整走一遍

两种方案都让业务层只面对 Parser,但扩展责任落点不同。简单工厂知道所有具体产品;工厂方法把每个具体产品的构造知识放到对应 Creator,系统边界再选择 Creator。

业务层提出“需要 Parser”
简单工厂路径: create(type) -> switch -> new Json/Xml
工厂方法路径: 先获得 ParserFactory 抽象
JsonParserFactory.create() -> JsonParser
XmlParserFactory.create() -> XmlParser
业务层统一调用 parser.parse()
新增 YAML 时比较修改中心分支与新增具体工厂的差异

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

六、模式边界与扩展成本

维度本题结论
稳定抽象Parser 稳定解析能力;工厂方法方案还稳定 ParserFactory.create()
主要变化点解析器类型、第三方库构造参数和初始化策略。
新增一种产品的动作简单工厂修改参数分支;工厂方法新增产品和具体 Creator。
不应放进工厂的职责解析过程本身与业务对解析结果的处理。
更轻或更合适的方案只有一种实现直接构造/注入;多种但稳定可用简单工厂。

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

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

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

记忆钩子:简单工厂是一个总窗口,工厂方法是可替换的多个创建窗口。

八、常见误区与追问

  • 误区:有一个叫 Factory 的类就是工厂方法模式。 要看是否有可替换 Creator 抽象并由具体 Creator 决定产品。
  • 误区:工厂方法的目的只是隐藏 new。 更重要的是把创建这一变化点也多态化。
  • 误区:业务层应根据具体产品做 instanceof 分支。 这说明产品接口没有覆盖稳定使用契约。
  • 追问:简单工厂的优点是什么? 类少、创建逻辑集中,产品少且稳定时更直观。
  • 追问:工厂方法的代价是什么? 具体工厂类增多,还需要装配或选择入口。
  • 追问:两者能否并存? 可以用简单/注册表入口选择具体 Factory,再由工厂方法创建产品。

九、加强记忆

简单工厂像一个“总窗口”,所有创建请求都到同一个方法里分流;工厂方法像“分窗口”,每个具体工厂只负责自己的产品。判断二者的核心不是有没有工厂类,而是新增产品时是改原工厂,还是加新工厂。