周日上午,小王大扫除。扫地拖地、收拾屋子,一通忙活下来,整个人瘫在沙发上。
然后饿了。
两个选择:自己做饭------买菜、洗菜、切配、掌勺、洗碗,一套下来,一两个小时就没了;或者打开外卖 App,点一份"麦辣鸡腿堡"。
他选了后者。三十分钟后,饭到了。

大扫除后的哲学家:能不下厨,就不下厨
这半小时里他没进厨房,不知道鸡腿肉是谁裹粉炸的、油温烧到多少度、酱料挤了几泵。他只管"要什么",不管"怎么造"。
今天聊的,就是这件事在代码里的样子:工厂模式(Factory Pattern)------把"造对象"这件事,从"用对象"的人手里拿走。系列第二篇,从小王这顿外卖开始。
01 · 自己 new,就是自己下厨
代码里的"自己下厨",长这样:
java
// 想吃麦辣鸡腿堡,自己 new:备料、腌制、油炸、组装全包
Meal meal = new McSpicyBurger("芝麻包", "炸鸡腿肉", "双倍辣酱");
// 构造器改一个参数,所有"自己下厨"的地方都得跟着改
自己 new:调用方和后厨死绑
想吃这个堡,你得先拿到配方:鸡腿肉腌多久、裹多厚的粉、油温烧到多少度------构造器的每个参数你都得懂。哪天 McSpicyBurger 改了构造签名,所有自己 new 过它的地方,全部跟着改。
一个类被 new 了一百次,就长出一百根绳子,把一百个调用方和它捆死在一起。
02 · 简单工厂:你报名字,后厨来做
麦当劳的做法,是把"怎么做"收进后厨。你只管报名字:
java
public class MealFactory {
// 后厨:你报名字,它来做
public static Meal create(String name) {
switch (name) {
case "麦辣鸡腿堡": return new McSpicyBurger();
case "板烧鸡腿堡": return new GrilledChickenBurger();
case "巨无霸": return new BigMac();
default: throw new IllegalArgumentException("这款堡不会");
}
}
}
// 你只管点,后厨的事不操心
Meal meal = MealFactory.create("麦辣鸡腿堡");
简单工厂:名字进,对象出
调用方从此不认识 McSpicyBurger,只认识两样东西:菜单(Meal 接口)和这家店(MealFactory)。创建和使用,分家了。

左边是你点外卖,右边是简单工厂
得挑明一个类比的失真点:点外卖会踩雷------图片上的堡又大又挺,到手就瘪了;代码里的工厂不会缩水,它返回的对象一定满足接口约定,说是什么就是什么。代码里的后厨,比外卖店守信用。
再问一步:菜单越写越长,会怎样?
03 · 工厂方法:一款汉堡,开一条产线
菜单从 3 款加到 30 款,后厨那个 switch 就写出 30 个分支。更要命的是:每上一款新品,都得改后厨------改错一行,全店停业。
这撞上了设计里的一条铁律:对扩展开放,对修改关闭。
解决办法:一款汉堡,开一条专属产线。 总部只定规矩------是条产线就得会出堡;具体做哪款,各产线自己说了算:
java
// 总部定的规矩:是条产线,就得会出堡
interface BurgerLine {
Meal cook();
}
// 麦辣鸡腿堡产线
class McSpicyLine implements BurgerLine {
public Meal cook() {
return new McSpicyBurger();
}
}
// 板烧鸡腿堡产线
class GrilledLine implements BurgerLine {
public Meal cook() {
return new GrilledChickenBurger();
}
}
工厂方法:上新品 = 新拉产线,老线不动
上新品 = 新拉一条产线,老产线的按钮一个不动。这就是工厂方法(Factory Method):把"造哪个"的决定,推迟给子类。
再问一步:光一个堡,够吗?
04 · 抽象工厂:套餐,讲究的是一整套配套
小王后来不点单品了,改点套餐。套餐的讲究不在单个堡,在搭配:麦当劳套餐配可口可乐,肯德基套餐配百事可乐------两大阵营签的都是排他协议,混不了。
配套不能错------麦当劳的杯子里倒出百事可乐,店员自己先慌了。
**抽象工厂(Abstract Factory)**管的,就是这一整套配套:
java
// 套餐工厂:一整套搭配,保证不串味
interface ComboFactory {
Burger burger(); // 堡
Fries fries(); // 薯条
Drink drink(); // 可乐
}
// 麦当劳套餐
class McDonaldsCombo implements ComboFactory {
public Burger burger() { return new McSpicyBurger(); }
public Fries fries() { return new McFries(); }
public Drink drink() { return new Coke(); }
}
// 肯德基套餐
class KfcCombo implements ComboFactory {
public Burger burger() { return new ZingerBurger(); }
public Fries fries() { return new KfcFries(); }
public Drink drink() { return new Pepsi(); }
}
抽象工厂:换套餐 = 换工厂,整套一次换
三种工厂讲完,一张图收个尾:

一家店 → 专门店一条街 → 套餐档口
05 · 这些"后厨",你天天在用
- 📅 JDK 的 Calendar.getInstance():你不管它背后是公历还是佛历,拿来就用------简单工厂的思路;
- 🔢 Integer.valueOf():《Effective Java》第 1 条就劝你:优先考虑静态工厂方法,别上来就 new;
- 📋 SLF4J 的 LoggerFactory.getLogger():你只管要个 logger,背后是 Logback 还是 Log4j2,不用你操心;
- 🌱 Spring 整个容器就是个超级大工厂:BeanFactory / ApplicationContext,你声明要什么 Bean,它负责造。
06 · 什么时候,别建工厂
说点边界。还是那个周日------小王擦桌子,拿块抹布蘸水就擦了。他不会为擦桌子先定义 CleaningTool 接口、写个 RagFactory、再 create 一块抹布出来。
自己就煎个蛋,犯不上先拉一条产线。
判断标准就一条:创建这件事,复杂吗?会变吗?
构造器一长串参数、创建要读配置、连资源、攒依赖------值得建工厂;
new ArrayList<>() 这种一行搞定、十年不变的------直接 new,别套。
工厂模式最常被骂的滥用,就是给每个类都配个工厂,类数量翻倍,读代码像剥洋葱。模式是工具,不是奖状------用了不涨工资。
最后跟系列第一篇呼应一下:工厂只管"怎么造",不管"造几个"。点两次外卖来两份,天经地义;想让全系统只有一份,那是单例的活,别混。
带走一句话:工厂模式 = 把"怎么做"关进后厨------你只管报名字(接口),做堡(new)的事,工厂包了。
好的代码和好的外卖一样:你享受结果,不必进厨房。
参考资料
- Erich Gamma 等,《设计模式:可复用面向对象软件的基础》(GoF 原著)------工厂方法、抽象工厂的原始出处
- 《Head First 设计模式》第 4 章------用披萨店讲工厂,本文"外卖店"类比的同乡
- Joshua Bloch,《Effective Java(第 3 版)》------第 1 条:考虑用静态工厂方法代替构造器(本文反例段的依据)
- Spring 官方文档 · IoC 容器