我不叫“工厂”,就不是工厂了吗?
前言
工厂模式(Factory Pattern)是设计模式中最常见、最基础的模式之一。往往在初学设计模式时,工厂模式是第一个接触到的模式。但在实际工作中,很多人对工厂模式的理解都停留在“有一个工厂类负责创建对象”这样的表面现象上,导致在使用时往往走入误区。
在实际工作之后慢慢积累经验,我发现工厂模式的本质其实并不在于“工厂”这个名字,所以我想带你重新思考一下设计模式的本质。
工厂实现
为了说明工厂模式的本质,我们先来看一个简单的代码例子
比如,我们有一个“支付”场景:
1 | package main |
灵魂拷问:为什么要这么做?
上面的代码,main 函数(客户)里为什么不直接这样写?
1 | // 客户直接 "new" |
这样不是更简单吗?为什么非要搞一个 NewPayment 函数绕一下?
答案是:为了“隔离”。
main 函数是“使用”支付功能的地方,而 AliPay{} 和 WechatPay{} 是“实现”支付功能的地方。
在没有工厂的情况下,“使用者”main 强依赖于“实现者”AliPay。
- 如果
AliPay的创建方式变了(比如NewAliPay(config)),main函数就必须修改。 - 如果
main函数想换成WechatPay,main函数也必须修改。
而 NewPayment 函数,就是那个“隔离层”。
使用工厂的好处是:
“使用者”(main)和“具体实现”(AliPay)解耦了。main只依赖“抽象”的IPayment接口和“工厂”NewPayment。至于
NewPayment内部是用switch还是if,是返回&AliPay{}还是&AliPay{config: ...},main根本不关心。
这,就是“抽象实现细节”的第一层。
一个不叫“工厂”的实现
NewPayment 函数虽然好,但它有个致命缺陷:违反了“开闭原则”。
如果我们要增加一种“银联支付”(UnionPay),我们必须去修改 NewPayment 函数的 switch 逻辑。
这在大型项目中往往会变得更加麻烦。我们希望的是,增加新功能时,不修改老代码。
我们来看一个在 Go 中更常见、更优雅的实现方式 —— “注册”。
1 | package main |
这个实现是不是很眼熟?
Go语言标准库 database/sql 就是这么干的。
sql.Register(driverName string, driver driver.Driver)sql.Open(driverName string, dataSourceName string)
sql.Open 就是那个“工厂”函数,它根本不知道有哪些数据库驱动(MySQL, PostgreSQL…),它只管去注册表里查 driverName。而各个驱动包通过 init() 函数把自己注册进去。
注意并不是所有的情况都适合使用 init 方法去隐式注册(有的人不喜欢),那么显示注册也是可以的,只要能达到“使用者”和“实现者”解耦的目的即可。
我不叫“工厂”,就不是工厂了吗?
现在,我们有了两个实现:
NewPayment:一个巨大的switch,它知道所有具体实现。GetPayment:一个map和一个查找函数,它不知道任何具体实现,它只知道一个“注册表”。
哪个是“工厂模式”?
答案是:它们都是。
NewPayment 是一个“集中式”的工厂。GetPayment (以及 RegisterPayment) 是一个“注册式”的工厂。
这引出了我们最终的结论:
工厂模式的本质,不是那个叫 Factory 的类或那个叫 NewXxx 的函数。
工厂模式的本质是:将“创建对象的具体过程”从“使用对象的地方”中剥离出来。
它是一种抽象。
它在“使用者”(如
main函数)和“实现者”(如AliPay结构体)之间,建立了一个隔离层至于是用
switch(简单工厂)、用“接口+实现”(工厂方法)、用“接口返回接口”(抽象工厂),还是用map(注册器)来实现的……这都不重要。
这些只是“术”(实现方式),而“道”(本质)是抽象和隔离。
所以,下次当你在代码里看到一个“注册中心”(Registry)、一个“提供者”(Provider)、一个“管理器”(Manager)或一个“服务定位器”(Service Locator)时,如果它的核心职责是根据某些条件创建并返回一个抽象接口的实例,那么,它就是工厂模式。
它叫不叫“工厂”,真的无所谓。
总结
其实再说的直白一点,只要你抽象了实现细节,让使用者不需要关心具体实现类的创建过程,就是在使用工厂模式了。而“工厂”只是说你抽象之后是一个创建“产品”的说法而已。
所以,工厂模式的核心思想是抽象实现细节,而这个思想是让你的代码更加解耦和灵活的方法之一。也是程序设计中非常重要的一个原则。
在实际中我做过大量的 Code Review 其实都是在做抽象这一件事,这个优化真的很重要,所以希望通过这篇文章能让你对工厂模式有一个新的认识。




