深入解析:设计模式(二)工厂方法模式 — 把创建权限下放给子类,像“可扩展的生产线”

工厂方法模式(Factory Method)是 GoF 设计模式中最经典、最核心的创建型模式之一。
它的目标简单而深刻:
将对象创建延迟到子类,使得创建逻辑可扩展、可替换、可按需分派,就像一条能不断扩展的生产线


一、工厂方法模式解决什么问题?

传统做法:直接 new 一个具体类

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()

⏱ 五、时序图(客户端如何与工厂交互)

ClientFactoryProductcreateSender()new ConcreteSender()send()ClientFactoryProduct

六、示例场景:消息发送系统(Email / SMS)

我们以“多渠道消息发送”为例,适合作为工厂模式教学场景。

1)产品接口(Product)

public interface MessageSender {
void send(String to, String content);
}

2)具体产品实现 A(Email)

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)

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...
}
}

七、简单工厂(适合小项目)

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 → 违反开闭原则

客户端调用

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)工厂接口

public interface MessageFactory {
MessageSender createSender();
}

2)具体工厂:EmailFactory

public class EmailFactory implements MessageFactory {
@Override
public MessageSender createSender() {
return new EmailSender("smtp.example.com");
}
}

3)具体工厂:SmsFactory

public class SmsFactory implements MessageFactory {
@Override
public MessageSender createSender() {
return new SmsSender("sms-provider");
}
}

4)客户端改造(依赖工厂)

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)抽象工厂与产品接口

public interface Button {
void render();
}
public interface Checkbox {
void render();
}
public interface WidgetFactory {
Button createButton();
Checkbox createCheckbox();
}

2)具体工厂:LightThemeFactory / DarkThemeFactory

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

@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)注册中心工厂

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/反射扫描)

public class EmailSender implements MessageSender {
static {
RegistryFactory.register("EMAIL", EmailSender.class);
}
@Override
public void send(String to, String content) { ... }
}

注意:生产环境推荐通过模块初始化或框架扫描注册,避免类加载副作用。


十三、总结

  • 工厂模式 是创建型模式的基石,适合把“变化点(创建)”从业务中抽离。
  • 工厂有多种形式:简单工厂、工厂方法、抽象工厂,按复杂度与扩展性选择。
  • 与 Spring / SPI / DI 配合可做出强大的插件化、配置化创建体系。
  • 切记:工厂职责是“创建”,不要把业务逻辑塞进去。
posted @ 2025-12-13 20:44  gccbuaa  阅读(0)  评论(0)    收藏  举报