工厂方法模式完全指南:从硬编码 new 到开闭原则的优雅实现

工厂方法模式是创建型模式里使用最广泛的一个,几乎每个稍微复杂一点的 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
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

单例模式完全指南:从双重检查锁到枚举单例,以及为什么大家都说"少用"

2026-5-15 11:35:34

技术教程

抽象工厂模式完全指南:让一族产品保持配套的设计利器

2026-5-15 11:35:34

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索