简单工厂模式
简单工厂(Simple Factory)不是 GoF 23 种设计模式之一,而是一种常见编程惯用法,也是理解工厂方法模式的起点。
从披萨店说起
想象你开了一家披萨店。最初只卖三种披萨,于是你在 orderPizza() 里写了 if-else——判断类型,new 对应的 Pizza,然后执行 prepare/bake/cut/box。
后来生意好了,需要下架希腊披萨、新增蛤蜊披萨,你不得不找到每一处 orderPizza() 逐一修改 if-else。改的地方越多,出 bug 的概率越高。
关键的问题在于:"创建 Pizza 的变化部分"和"处理 Pizza 的不变部分"混在了一起。
解决方案:把创建逻辑提取到一个专门的工厂类 SimplePizzaFactory 中,整个系统中只有这一处 new 具体 Pizza。
🔍 定义
简单工厂由一个专门的工厂类负责创建各种产品对象,客户端只需告诉工厂"我要哪种类型",无需关心具体类的实例化细节。
⚠️ 不使用简单工厂存在的问题
| SimpleFactoryBadExample.java |
|---|
| package com.example.creational.simple_factory;
/**
* 简单工厂模式 - 反例
* 问题:客户端直接依赖具体类,新增通知渠道需要找到所有调用点逐一修改
*/
public class SimpleFactoryBadExample {
public static void main(String[] args) {
SimpleFactoryBadExample demo = new SimpleFactoryBadExample();
demo.notify("email", "验证码:123456");
demo.notify("sms", "验证码:123456");
demo.notify("push", "验证码:123456");
// 新增微信通知?每个 notify 方法都要改 ❌
}
// ❌ 客户端直接依赖具体类,新增通知渠道需要找到所有调用点逐一修改
public void notify(String type, String msg) {
if ("email".equals(type)) {
new EmailSenderBad().send(msg);
} else if ("sms".equals(type)) {
new SmsSenderBad().send(msg);
} else if ("push".equals(type)) {
new PushSenderBad().send(msg);
}
// 新增微信通知?每个 notify 方法都要改 ❌
}
}
class EmailSenderBad {
public void send(String message) { System.out.println("[Email] " + message); }
}
class SmsSenderBad {
public void send(String message) { System.out.println("[SMS] " + message); }
}
class PushSenderBad {
public void send(String message) { System.out.println("[Push] " + message); }
}
|
🏗️ 设计模式结构(披萨店)
%%{init: {'themeVariables': {'noteBkgColor': 'transparent', 'noteBorderColor': '#768390'}}}%%
classDiagram
classDef default fill:transparent,stroke:#768390
class Pizza {
<<abstract>>
#name: String
+prepare() void
+bake() void
+cut() void
+box() void
}
class CheesePizza {
}
class GreekPizza {
}
class ClamPizza {
}
class SimplePizzaFactory {
+createPizza(type) Pizza
}
class PizzaStore {
-factory: SimplePizzaFactory
+orderPizza(type) Pizza
}
Pizza <|-- CheesePizza
Pizza <|-- GreekPizza
Pizza <|-- ClamPizza
SimplePizzaFactory ..> Pizza
PizzaStore o--> SimplePizzaFactory
note for Pizza "抽象产品(AbstractProduct)"
note for CheesePizza "具体产品(ConcreteProduct)"
note for SimplePizzaFactory "工厂(Factory)"
note for PizzaStore "客户端(Client)"
工厂类集中管理 new 操作,PizzaStore 只依赖 SimplePizzaFactory 和抽象 Pizza。
💻 设计模式举例说明
| SimpleFactoryExample.java |
|---|
| package com.example.creational.simple_factory;
/**
* 简单工厂模式 - 正例
* 工厂类集中管理 new 操作,客户端只依赖接口 NotificationSender
*/
public class SimpleFactoryExample {
public static void main(String[] args) {
// ✅ 调用方只依赖接口,不依赖具体类
NotificationSender sender = SenderFactory.create("email");
sender.send("验证码:123456"); // [Email] 验证码:123456
sender = SenderFactory.create("sms");
sender.send("验证码:123456"); // [SMS] 验证码:123456
sender = SenderFactory.create("push");
sender.send("验证码:123456"); // [Push] 验证码:123456
}
}
// 产品接口
interface NotificationSender {
void send(String message);
}
// 具体产品:邮件
class EmailSender implements NotificationSender {
@Override
public void send(String message) { System.out.println("[Email] " + message); }
}
// 具体产品:短信
class SmsSender implements NotificationSender {
@Override
public void send(String message) { System.out.println("[SMS] " + message); }
}
// 具体产品:推送
class PushSender implements NotificationSender {
@Override
public void send(String message) { System.out.println("[Push] " + message); }
}
// 工厂类:集中管理对象创建
class SenderFactory {
public static NotificationSender create(String type) {
return switch (type) {
case "email" -> new EmailSender();
case "sms" -> new SmsSender();
case "push" -> new PushSender();
default -> throw new IllegalArgumentException("未知渠道:" + type);
};
}
}
|
静态工厂方法
SimplePizzaFactory.createPizza() 也可以改成静态方法——直接 SimplePizzaFactory.createPizza("cheese"),称为**静态工厂**(Static Factory Method)。代价是无法通过子类化来修改工厂的行为。
⚖️ 优缺点
优点:
- 对象创建逻辑集中在一处,客户端无需了解具体类名
- 客户端只依赖抽象,符合依赖倒置原则
缺点:
- 新增产品类型需要修改工厂的 switch(违反**开闭原则**)
- 项目变大后工厂方法会越来越臃肿
🔗 与其它模式的关系
| 模式 |
扩展方式 |
是否违反 OCP |
| 简单工厂 |
修改工厂类的 switch |
❌ 违反 OCP |
| 工厂方法 |
新增工厂子类 |
✅ 不违反 OCP |
| 抽象工厂 |
新增工厂实现类 |
✅ 不违反 OCP |
工厂方法是简单工厂的演进——将"决定创建哪种对象"的逻辑从 switch 分支改为由子类继承实现,避免修改已有代码。
🗂️ 应用场景
- 产品类型相对固定,不会频繁新增
- 需要将对象创建逻辑与使用逻辑分离,降低客户端耦合
- JDK:
Calendar.getInstance()、NumberFormat.getInstance() 内部使用了类似思路
🏭 工业视角
简单工厂的本质:封装变化,隔离创建与使用
简单工厂不是为了"高大上的设计模式",而是一个实用的**封装变化**手段。当对象的创建逻辑(如根据文件后缀选择 Parser)散落在调用方时,每次新增类型都要修改调用方代码。把创建逻辑收拢到工厂类,调用方只需传入类型标识,其余与它无关。
| 简单工厂:将创建逻辑聚拢到一处 |
|---|
| // 工厂类统一管理 Parser 的创建,调用方不依赖任何具体 Parser 类
public class RuleConfigParserFactory {
private static final Map<String, IRuleConfigParser> cachedParsers = new HashMap<>();
static {
cachedParsers.put("json", new JsonRuleConfigParser());
cachedParsers.put("xml", new XmlRuleConfigParser());
cachedParsers.put("yaml", new YamlRuleConfigParser());
}
public static IRuleConfigParser createParser(String format) {
return cachedParsers.get(format.toLowerCase());
}
}
|
这里同时利用了**缓存**:Parser 无状态,可以复用同一实例,避免重复创建。
何时不需要工厂:警惕过度设计
如果创建对象的逻辑只是一行 new FooClass(),没有任何条件判断或参数处理,直接 new 即可,引入工厂只是增加了一个没有实际价值的间接层。
判断是否需要工厂的关键问题:
- 创建逻辑是否**会变化**(新增类型、切换实现)?
- 创建逻辑是否**足够复杂**(多个条件分支、需要缓存、需要校验)?
- 是否需要**对调用方隐藏**具体类名(依赖倒置)?
三个都是"否",就直接 new。
简单工厂 vs 工厂方法的选择
如果创建逻辑简单(几个 if-else),且新增类型不频繁,简单工厂已经足够,不需要引入工厂方法的多态体系。
工厂方法适合创建逻辑本身就复杂、不同类型的创建过程差异很大的场景。