工厂方法模式(Factory Method)是 GoF 设计模式中最经典、最核心的创建型模式之一。
它的目标简单而深刻:
将对象创建延迟到子类,使得创建逻辑可扩展、可替换、可按需分派,就像一条能不断扩展的生产线。
🏭 一、工厂方法模式解决什么问题?
传统做法:直接 new 一个具体类
java
MessageSender sender = new EmailSender();
这会带来几个问题:
| 问题 | 描述 |
|---|---|
| 强依赖具体类 | 客户端依赖 EmailSender,一旦替换实现,就必须改客户端 |
| 创建逻辑分散 | 连接参数、构造参数散落在代码中,难维护 |
| 扩展不友好 | 想增加新的实现(如 PushSender),必须修改业务代码(违背 OCP) |
| 配置难以驱动 | 想让创建逻辑由配置(YAML/DB)决定非常困难 |
✨ 工厂方法模式解决什么?
- 把 创建逻辑集中管理
- 用 接口隔离调用者与具体类
- 让扩展变得简单:添加新产品 → 添加新工厂 → 客户端无需改动
- 能非常自然地结合 Spring / SPI / 配置中心
🔑 二、工厂方法的核心思想:把"创建权"交给子类
工厂方法典型定义:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法让对象的创建延迟到子类。
重点词:
- "接口":约束创建
- "子类":扩展入口
- "延迟到子类":允许动态替换
工厂方法本质就是一个 可扩展创建点(Extension Point)。
🧭 三、整体流程图(以工厂方法为例)
createProduct Client Factory ConcreteProduct
核心思想:客户端依赖工厂接口,而不是具体产品。
🏷 四、UML 类图(工厂方法)
<<interface>> Product +use() ConcreteProductA +use() ConcreteProductB +use() <<abstract>> Factory +createProduct() ConcreteFactoryA +createProduct() ConcreteFactoryB +createProduct()
⏱ 五、时序图(客户端如何与工厂交互)
Client Factory Product createSender() new ConcreteSender() send() Client Factory Product
💡 六、示例场景:消息发送系统(Email / SMS)
我们以"多渠道消息发送"为例,适合作为工厂模式教学场景。
1)产品接口(Product)
java
public interface MessageSender {
void send(String to, String content);
}
2)具体产品实现 A(Email)
java
public class EmailSender implements MessageSender {
private final String smtpServer;
public EmailSender(String smtpServer) {
this.smtpServer = smtpServer;
}
@Override
public void send(String to, String content) {
System.out.println("[Email] to=" + to + " via " + smtpServer + " content=" + content);
// 调用真实 SMTP 客户端逻辑...
}
}
3)具体产品实现 B(SMS)
java
public class SmsSender implements MessageSender {
private final String provider;
public SmsSender(String provider) {
this.provider = provider;
}
@Override
public void send(String to, String content) {
System.out.println("[SMS] to=" + to + " via " + provider + " content=" + content);
// 调用真实 SMS SDK...
}
}
🧪 七、简单工厂(适合小项目)
java
public class MessageSenderFactory {
public static MessageSender create(String type) {
switch (type) {
case "EMAIL":
return new EmailSender("smtp.example.com");
case "SMS":
return new SmsSender("sms-provider");
default:
throw new IllegalArgumentException("Unknown type: " + type);
}
}
}
优点:简单
缺点:新增类型要修改 switch → 违反开闭原则
客户端调用
java
public class Main {
public static void main(String[] args) {
MessageSender email = MessageSenderFactory.create("EMAIL");
email.send("alice@example.com", "Hello Alice");
MessageSender sms = MessageSenderFactory.create("SMS");
sms.send("+8613712345678", "Hello via SMS");
}
}
🧩 八、工厂方法(Factory Method)实现
1)工厂接口
java
public interface MessageFactory {
MessageSender createSender();
}
2)具体工厂:EmailFactory
java
public class EmailFactory implements MessageFactory {
@Override
public MessageSender createSender() {
return new EmailSender("smtp.example.com");
}
}
3)具体工厂:SmsFactory
java
public class SmsFactory implements MessageFactory {
@Override
public MessageSender createSender() {
return new SmsSender("sms-provider");
}
}
4)客户端改造(依赖工厂)
java
public class Client {
private final MessageFactory factory;
public Client(MessageFactory factory) {
this.factory = factory;
}
public void doSend() {
MessageSender sender = factory.createSender();
sender.send("bob@example.com", "content");
}
public static void main(String[] args) {
Client c = new Client(new EmailFactory());
c.doSend();
}
}
✔️ 扩展方式
新增 PushSender → 新建 PushFactory → 完全无需修改客户端
🏢 九、抽象工厂示例
1)抽象工厂与产品接口
java
public interface Button {
void render();
}
public interface Checkbox {
void render();
}
public interface WidgetFactory {
Button createButton();
Checkbox createCheckbox();
}
2)具体工厂:LightThemeFactory / DarkThemeFactory
java
public class LightButton implements Button {
@Override
public void render() {
System.out.println("Render light button");
}
}
public class DarkButton implements Button {
@Override
public void render() {
System.out.println("Render dark button");
}
}
public class LightFactory implements WidgetFactory {
@Override
public Button createButton() {
return new LightButton();
}
@Override
public Checkbox createCheckbox() {
return () -> System.out.println("Render light checkbox");
}
}
public class DarkFactory implements WidgetFactory {
@Override
public Button createButton() {
return new DarkButton();
}
@Override
public Checkbox createCheckbox() {
return () -> System.out.println("Render dark checkbox");
}
}
🧩 十、工厂方法 vs 简单工厂
| 点 | 简单工厂 | 工厂方法 |
|---|---|---|
| 扩展性 | ❌ 修改 switch | ✅ 增加类即可 |
| 是否遵守开闭原则 | ❌ 不遵守 | ✔️ 完全遵守 |
| 产品数量少 | ✔️ 很适用 | 普通 |
| 产品扩展多 | 不适用 | ✔️ 强推荐 |
| 是否由子类决定产品类型 | 否 | 是(关键差异) |
🌿 十一、Spring 中的常见做法
1)把实现注册为 Bean 并注入 Map
java
@Component("emailSender")
public class EmailSender implements MessageSender { ... }
@Component("smsSender")
public class SmsSender implements MessageSender { ... }
@Service
public class MessageService {
private final Map<String, MessageSender> senders;
@Autowired
public MessageService(Map<String, MessageSender> senders) {
this.senders = senders;
}
public void send(String type, String to, String content) {
MessageSender sender = senders.get(type.toLowerCase());
if (sender == null) throw new IllegalArgumentException("no sender");
sender.send(to, content);
}
}
2)注解 + 自动注册
自定义注解并通过 BeanPostProcessor 或 Spring 的 @Conditional 在启动时注册/筛选策略,适合复杂企业级场景(代码略,思路在于利用 Spring 扫描并把带注解的实现按注解值注册到 Map)。
⚠️十一、常见误区与反模式
| 误区 | 为什么错 |
|---|---|
| 把业务逻辑放进工厂 | 工厂只负责"创建",不负责"行为" |
| 工厂方法用得太多 | 简单场景用简单工厂更合适 |
| 工厂类 new 来 new 去 | 工厂本身应该是单例/被容器管理 |
| 把工厂方法和抽象工厂混淆 | 抽象工厂是"产品族",工厂方法是"单一产品等级结构" |
| 把策略模式当工厂 | 策略替换行为,工厂替换创建对象 |
🛠 十二、基于反射的可注册工厂(插件化)
1)注册中心工厂
java
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class RegistryFactory {
private static final Map<String, Class<? extends MessageSender>> REG = new ConcurrentHashMap<>();
public static void register(String key, Class<? extends MessageSender> impl) {
REG.put(key, impl);
}
public static MessageSender create(String key) {
Class<? extends MessageSender> cls = REG.get(key);
if (cls == null) throw new IllegalArgumentException("no impl");
try {
return cls.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
2)在实现类静态块中注册(或通过 SPI/反射扫描)
java
public class EmailSender implements MessageSender {
static {
RegistryFactory.register("EMAIL", EmailSender.class);
}
@Override
public void send(String to, String content) { ... }
}
注意:生产环境推荐通过模块初始化或框架扫描注册,避免类加载副作用。
🔚 十三、总结
- 工厂模式 是创建型模式的基石,适合把"变化点(创建)"从业务中抽离。
- 工厂有多种形式:简单工厂、工厂方法、抽象工厂,按复杂度与扩展性选择。
- 与 Spring / SPI / DI 配合可做出强大的插件化、配置化创建体系。
- 切记:工厂职责是"创建",不要把业务逻辑塞进去。