静态工厂方法和工厂方法模式有什么区别?
简化版
静态工厂方法通常是类上的 of()、valueOf()、getInstance() 这类命名创建方法,本质是 API 写法;工厂方法模式则是把创建动作延迟到子类或具体工厂,通过多态扩展产品创建。前者偏“更好地封装构造器”,后者偏“让创建逻辑可扩展、可替换”。
详细版
静态工厂方法不是 GoF 工厂方法模式的同义词。它通常指一个静态方法返回对象,比如 LocalDate.of(2026, 8, 1)、Integer.valueOf(100)。它可以有名称、可以缓存对象、可以返回子类型,也可以隐藏构造细节。
工厂方法模式强调的是抽象创建接口和多态扩展:客户端依赖 Creator 或 Factory 抽象,具体创建哪种产品由具体工厂决定。新增产品时,往往新增一个工厂类,而不是修改原有大分支。
| 维度 | 静态工厂方法 | 工厂方法模式 |
|---|---|---|
| 本质 | 命名的静态创建 API | 多态创建模式 |
| 扩展方式 | 修改静态方法内部逻辑较常见 | 新增具体工厂/产品 |
| 是否依赖继承/接口 | 不一定 | 通常依赖抽象产品和抽象工厂 |
| 高频场景 | 值对象、缓存、语义化构造 | 插件、框架扩展、产品创建解耦 |
面试时可以这样答:静态工厂方法是构造器替代方案,工厂方法模式是创建职责解耦方案。两者都能封装创建,但扩展能力和设计意图不同。
完整版教学
一、先把名字拆开,不要被“工厂方法”四个字绕住
“静态工厂方法”里的工厂,更多是 API 命名习惯:不用 new,而是通过一个有语义的静态方法拿对象。“工厂方法模式”里的工厂,是设计模式角色:由抽象创建者声明创建方法,具体创建者决定实例化哪种产品。
示意:
静态工厂方法:User.of(name, age)
工厂方法模式:ParserFactory.createParser() -> JsonParser / XmlParser
两者都能隐藏构造细节,但前者不一定带来开放扩展结构,后者的核心就是把创建逻辑从使用方手里拿出来,并交给可替换的工厂层。
二、静态工厂方法解决什么问题
构造器只能用类名作为入口,无法表达创建语义。静态工厂方法可以用名字说明意图,比如 of 表示按参数创建,from 表示类型转换,valueOf 可能带缓存,getInstance 可能复用对象。
public final class Money {
private final long cents;
private Money(long cents) {
this.cents = cents;
}
public static Money yuan(long yuan) {
return new Money(yuan * 100);
}
public static Money cents(long cents) {
return new Money(cents);
}
}
如果传入数字 100,构造器 new Money(100) 不知道单位;Money.yuan(100) 和 Money.cents(100) 则把语义说清楚。这里并没有复杂多态,只是 API 更清楚。
三、工厂方法模式解决什么问题
工厂方法模式的重点是让客户端依赖抽象,而不是依赖具体产品创建。比如系统支持 JSON、XML、YAML 三种解析器,业务代码只关心 Parser 接口,不关心具体类怎么 new。
public interface Parser {
Document parse(String text);
}
public interface ParserFactory {
Parser createParser();
}
public class JsonParserFactory implements ParserFactory {
public Parser createParser() {
return new JsonParser();
}
}
新增 TomlParserFactory 时,不必修改使用 ParserFactory 抽象的业务流程。工厂方法的价值来自多态扩展,而不是来自“方法是静态还是实例”。
四、为什么静态方法常常不满足开闭原则
静态工厂方法也能根据参数返回不同子类,但如果它内部写了大分支,每次新增类型都要修改这个静态方法。比如新增一种支付渠道,需要改 Payment.create(type),这对频繁扩展的业务不友好。
public static Payment create(String type) {
if ("wechat".equals(type)) return new WechatPayment();
if ("alipay".equals(type)) return new AlipayPayment();
throw new IllegalArgumentException(type);
}
假设一年新增 12 个渠道,这个方法会被反复修改 12 次,测试也要反复覆盖旧分支。若渠道由插件或独立团队扩展,注册表工厂、工厂方法或容器装配通常更合适。
记忆钩子:静态工厂方法让构造更好读,工厂方法模式让创建更好扩展。
五、两者可以组合使用
两种概念不是互斥的。具体工厂内部也可以使用静态工厂方法,静态工厂方法也可以返回工厂对象。关键是不要把组合后的代码误认为两者没有区别。
例如:
public final class ParserFactories {
public static ParserFactory json() {
return new JsonParserFactory();
}
}
这里 ParserFactories.json() 是静态工厂方法,它返回的是一个具体工厂;而 JsonParserFactory.createParser() 才是在执行工厂方法模式里的产品创建职责。面试中能说出这一层,说明不是只背名词。
六、选择时看扩展频率和抽象边界
如果对象类型固定、创建逻辑简单、只是想隐藏构造器或做缓存,静态工厂方法很好。如果产品族持续扩展、创建逻辑由不同模块负责、使用方需要依赖抽象,工厂方法模式更合适。
| 判断问题 | 更偏静态工厂方法 | 更偏工厂方法模式 |
|---|---|---|
| 类型数量 | 少且稳定 | 多且常扩展 |
| 使用方 | 可以知道返回类或抽象 | 应只依赖抽象产品 |
| 创建逻辑 | 简单集中 | 由不同工厂分散承担 |
| 测试替换 | 不强 | 需要替换工厂或产品 |
一个实用标准是:如果新增类型必须改老代码,就不是很开放;如果新增类型只需要新增类和注册关系,扩展性会更好。
七、常见误区与追问
- 误区:只要方法名叫 create 就是工厂方法模式。 是否是模式要看有没有抽象创建职责和多态扩展,不看方法名。
- 误区:静态工厂方法一定比构造器高级。 简单对象用构造器也清楚,静态工厂适合需要命名语义、缓存或隐藏实现时。
- 误区:工厂方法模式一定不能有 static。 static 可以作为辅助入口,但模式核心仍是抽象工厂方法和具体工厂多态。
- 误区:静态工厂方法天然符合开闭原则。 内部大分支仍会导致新增类型时修改老代码。
- 追问:
Integer.valueOf属于哪类? 它是静态工厂方法,带缓存语义,不是典型 GoF 工厂方法模式。 - 追问:静态工厂方法能返回接口吗? 可以,这也是它隐藏实现的优点之一。
- 追问:什么时候要从静态工厂演进到工厂方法? 当类型扩展频繁、创建逻辑分散、调用方需要替换工厂时。
八、加强记忆
这题记住两条线:静态工厂方法是“构造器 API 的改良”,工厂方法模式是“创建职责的多态解耦”。前者让创建更有语义,后者让新增产品更少改旧代码;判断时看扩展频率、抽象边界和测试替换需求。