工厂方法模式是创建型模式里使用最广泛的一个,几乎每个稍微复杂一点的 Java/C# 项目都能找到它的身影。但很多人把"new 一个对象的辅助方法"叫做工厂,这只是个"静态方法",离真正的工厂方法模式还差一点。这篇文章把工厂方法模式从动机讲到落地,讲清楚它和简单工厂、抽象工厂的差别,以及为什么它在依赖注入大行其道的今天仍然重要。
为什么需要工厂方法
看一段普通代码:
public class Logistics {
public void planDelivery(Order order) {
Truck truck = new Truck(); // 硬编码 Truck
truck.deliver(order);
}
}
问题在于 new Truck() 把"创建什么"和"使用它干什么"绑死了。哪天要支持海运,Logistics 整个类要改;支持空运再改一遍。每加一种运输方式,你都在动这个本应稳定的类。
工厂方法模式提出:把"创建对象"这个动作从调用者剥离出去,做成一个方法,让子类去决定创建什么。
// 抽象产品
interface Transport {
void deliver(Order order);
}
class Truck implements Transport {
public void deliver(Order o) { System.out.println("地面运输 " + o); }
}
class Ship implements Transport {
public void deliver(Order o) { System.out.println("海运 " + o); }
}
class Plane implements Transport {
public void deliver(Order o) { System.out.println("空运 " + o); }
}
// 抽象工厂(创建者)
abstract class Logistics {
// 这个就是"工厂方法":由子类决定创建什么
protected abstract Transport createTransport();
// 模板方法:统一流程,具体类型由 createTransport 决定
public void planDelivery(Order order) {
Transport t = createTransport();
t.deliver(order);
}
}
// 具体工厂
class RoadLogistics extends Logistics {
protected Transport createTransport() { return new Truck(); }
}
class SeaLogistics extends Logistics {
protected Transport createTransport() { return new Ship(); }
}
class AirLogistics extends Logistics {
protected Transport createTransport() { return new Plane(); }
}
// 使用
Logistics logistics = new RoadLogistics(); // 选择具体策略
logistics.planDelivery(order); // 流程完全一样
关键变化:Logistics.planDelivery 不再需要知道有哪些运输方式存在,加新的 RailLogistics 不需要改任何老代码,只需新增一个子类。这就是"对扩展开放、对修改关闭"(开闭原则)的具体实现。
简单工厂 vs 工厂方法 vs 抽象工厂
这三个常被混为一谈,实际区别清晰:
简单工厂(不是 GoF 模式,但常见)
// 用 switch 或 if 决定创建什么
class TransportFactory {
public static Transport create(String type) {
switch (type) {
case "truck": return new Truck();
case "ship": return new Ship();
case "plane": return new Plane();
default: throw new IllegalArgumentException();
}
}
}
Transport t = TransportFactory.create("truck");
简单工厂的问题:每加一种产品要改 create 方法 —— 违反开闭原则。它适合"产品种类相对固定"的场景。
工厂方法(GoF)
用继承来扩展:加新产品 = 加新工厂子类。结构如本文上面的例子。
抽象工厂(GoF,下一篇详细讲)
一个工厂负责创建一族相关产品(GUI 工厂同时创建 Button、TextField、Checkbox),保证它们风格一致。
实战 1:解析器工厂
假设业务要支持多种文件格式的导入,每种格式需要不同的解析器:
// 产品
interface Parser {
List<Record> parse(InputStream in);
}
class CsvParser implements Parser {
public List<Record> parse(InputStream in) { /* CSV 解析 */ }
}
class JsonParser implements Parser {
public List<Record> parse(InputStream in) { /* JSON 解析 */ }
}
class XmlParser implements Parser {
public List<Record> parse(InputStream in) { /* XML 解析 */ }
}
// 创建者
abstract class ImportProcessor {
protected abstract Parser createParser();
public ImportResult process(InputStream in) {
Parser parser = createParser();
List<Record> records = parser.parse(in);
// 后续:校验、入库、回调...
return new ImportResult(records.size());
}
}
class CsvImport extends ImportProcessor {
protected Parser createParser() { return new CsvParser(); }
}
class JsonImport extends ImportProcessor {
protected Parser createParser() { return new JsonParser(); }
}
整套设计的好处:process 里所有"非解析"的逻辑(校验、入库、回调)被复用,只有创建解析器这一步交给子类。新加 YAML、Excel 支持,各自加一个 ImportProcessor 子类即可。
实战 2:用工厂方法实现"按需替换"
// 测试场景:用 mock parser 替换真实 parser
class TestImport extends ImportProcessor {
protected Parser createParser() {
return new MockParser(predefinedRecords); // 测试可控
}
}
// 生产场景:从配置选 parser
class ConfigurableImport extends ImportProcessor {
private final String type;
public ConfigurableImport(String type) { this.type = type; }
protected Parser createParser() {
return switch (type) {
case "csv" -> new CsvParser();
case "json" -> new JsonParser();
default -> throw new IllegalStateException();
};
}
}
各语言里的工厂方法
Python
from abc import ABC, abstractmethod
class Transport(ABC):
@abstractmethod
def deliver(self, order): ...
class Truck(Transport):
def deliver(self, order): print(f"truck {order}")
class Ship(Transport):
def deliver(self, order): print(f"ship {order}")
class Logistics(ABC):
@abstractmethod
def create_transport(self): ...
def plan_delivery(self, order):
self.create_transport().deliver(order)
class RoadLogistics(Logistics):
def create_transport(self): return Truck()
class SeaLogistics(Logistics):
def create_transport(self): return Ship()
# Python 的优势:函数也是对象,可以直接传"工厂函数"
class Logistics2:
def __init__(self, transport_factory):
self.transport_factory = transport_factory
def plan_delivery(self, order):
self.transport_factory().deliver(order)
# 用法:把构造函数当作工厂直接传进去 —— 更轻量
l = Logistics2(Truck)
l.plan_delivery("a")
Python 因为"类即对象",很多场景不需要单独写工厂类。把构造函数当作工厂函数传递,代码量缩到极简,但本质上还是工厂方法的思想。
TypeScript
interface Transport {
deliver(order: string): void;
}
class Truck implements Transport {
deliver(o: string) { console.log('truck', o); }
}
class Ship implements Transport {
deliver(o: string) { console.log('ship', o); }
}
abstract class Logistics {
protected abstract createTransport(): Transport;
planDelivery(order: string) {
this.createTransport().deliver(order);
}
}
class RoadLogistics extends Logistics {
protected createTransport() { return new Truck(); }
}
// 函数式风格:工厂就是个返回 Transport 的函数
type TransportFactory = () => Transport;
class Logistics2 {
constructor(private factory: TransportFactory) {}
planDelivery(order: string) {
this.factory().deliver(order);
}
}
new Logistics2(() => new Truck()).planDelivery('x');
Go:没有继承,用函数
package main
type Transport interface {
Deliver(order string)
}
type Truck struct{}
func (t *Truck) Deliver(order string) { fmt.Println("truck", order) }
type Ship struct{}
func (s *Ship) Deliver(order string) { fmt.Println("ship", order) }
// Go 没有抽象类,工厂方法用函数表达
type TransportFactory func() Transport
func PlanDelivery(factory TransportFactory, order string) {
factory().Deliver(order)
}
// 调用方
PlanDelivery(func() Transport { return &Truck{} }, "a")
参数化工厂:在工厂方法里传参
有时候同一个创建者根据参数决定创建什么:
class TransportFactory {
// 经典写法:子类化版本臃肿,这种"参数化工厂"折中实用
public Transport create(TransportType type, Config cfg) {
return switch (type) {
case TRUCK -> new Truck(cfg.getMaxWeight());
case SHIP -> new Ship(cfg.getCapacity());
case PLANE -> new Plane(cfg.getFuelLimit());
};
}
}
注意这种写法已经不再是严格 GoF 意义上的工厂方法 —— 它退化成了简单工厂。但工程里很常见,尤其是"创建逻辑稍微复杂、产品类型有限"的场景。
静态工厂方法:JDK 里最常见的"工厂"
JDK 里大量"工厂方法"其实是静态工厂方法:
List<Integer> a = List.of(1, 2, 3); // 静态工厂
Map<String, Integer> m = Map.of("a", 1, "b", 2); // 静态工厂
Path p = Path.of("/tmp"); // 静态工厂
Integer i = Integer.valueOf(42); // 静态工厂(缓存常用值)
Optional<String> o = Optional.of("hello"); // 静态工厂
// 命名约定 vs new 的区别:
// of —— 把参数包装成实例(可能复用缓存)
// from —— 类型转换
// valueOf —— 与参数等价的实例(支持缓存)
// newInstance / create —— 总是新建
// getInstance —— 单例
Joshua Bloch 在《Effective Java》第 1 条就推荐"用静态工厂方法代替构造器":可以有有意义的名字、可以返回子类型、可以做缓存、可以延迟加载。这种"广义工厂方法"在现代 Java 设计里几乎无处不在。
工厂方法与依赖注入的关系
有人问:有了 Spring / Guice 这种 DI 容器,工厂方法还需要吗?
答:需要,而且二者互补。DI 容器解决的是"对象实例 + 依赖关系"的管理(由容器在启动期连线),工厂方法解决的是"运行时按业务条件创建对象"。看场景:
// DI 解决:Service 依赖 Repository
@Service
public class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) { this.repo = repo; }
}
// 工厂方法解决:运行时按用户角色创建不同的策略对象
@Component
public class StrategyFactory {
public PaymentStrategy create(User user) {
if (user.isVip()) return new VipStrategy();
if (user.isMember()) return new MemberStrategy();
return new RegularStrategy();
}
}
第二种情况下,你不能让 DI 容器直接注入一个 PaymentStrategy —— 因为每次请求要根据用户决定用哪种。这时工厂方法依然是正确的设计。Spring 也提供 ObjectFactory / Provider 让你拿到"按需创建对象"的能力。
常见坑
坑 1:工厂方法和继承耦合过紧。 严格 GoF 工厂方法要求"用继承做扩展"。但子类爆炸是个真实问题 —— 10 种解析器就要 10 个 Importer 子类。工程上更灵活的是用"组合 + 函数式工厂"(参考前面 TS 和 Python 的例子)。
坑 2:工厂的工厂的工厂。 过度抽象的真实案例:为了避免 hardcoded "选择哪个工厂",再写一个 FactoryFactory。除非业务确实需要,否则停止套娃 —— 每多一层间接,可读性下降一点,debug 难度上升一截。
坑 3:工厂方法成了 catch-all。 工厂方法的本意是"对象创建逻辑封装",但常被滥用成"任何静态方法堆放地"。一个工厂类里既有 create(...) 也有 parse(...) 也有 convert(...) 时,它就不再是工厂,而是过气的 "Utility 类"。
写在最后
工厂方法不是炫技,它解决的是一个非常具体的问题:调用者不应该知道具体类。一旦你写出 new Foo(),这一行代码就被绑死到 Foo 上 —— 哪天要扩展 / 替换 / mock,这一行都是阻力。工厂方法把这个绑定推后到子类或参数,给你保留扩展和测试的空间。
给一个判断标准:当一个对象的"具体类型"会变,但"如何使用它"不变时,就是工厂方法登场的时刻。日志器、解析器、支付策略、运输方式、AI 模型推理器、ORM 数据库方言适配 —— 这些场景里你都能看到工厂方法在悄悄工作。读到这里,你下次写 new 之前应该会多想一秒:这个 new 该不该藏起来。
—— 别看了 · 2026