模式对比与选型:单例 vs 工厂 vs 观察者

说实话,这三个模式是日常开发中最常用的几个。我见过不少团队,一上来就堆设计模式,结果代码反而更难维护。今天咱们就聊聊,到底什么时候该用哪个。

先给个结论:没有银弹。每个模式解决的是不同维度的问题。选错了,代码就会变得拧巴。

一、核心区别一句话说清

模式 核心关注点 解决什么问题 典型场景
单例 实例唯一性 全局只有一个对象 配置管理器、日志器、线程池
工厂 对象创建 解耦创建与使用 数据库连接、UI组件创建
观察者 对象间通信 一对多通知 事件系统、消息推送、UI更新

你想想看,这三个模式其实不在同一个维度上。单例管的是「数量」,工厂管的是「创建」,观察者管的是「通信」。所以选型时,先问自己:我现在遇到的是哪个维度的问题?

二、何时使用单例

我个人习惯,只有在两种情况下才用单例:

  • 资源必须唯一:比如数据库连接池,你不可能创建两个连接池去管理同一组连接,那会乱套。
  • 状态需要全局共享:比如应用的配置信息,所有模块都要读同一份配置。

核心判断标准:如果系统中存在两个实例会导致逻辑错误或资源冲突,那就用单例。

我在项目中遇到过一个问题:一个日志模块被多个线程同时调用,结果日志文件被反复打开关闭。后来改成单例,所有线程共享同一个日志实例,问题就解决了。

避坑指南:我曾经在一个微服务里滥用单例,把每个服务的状态都放在单例里。结果服务扩容时,多个实例之间状态不同步,出了大问题。记住:单例只在进程内有效,跨进程或跨服务就别想了。

三、何时使用工厂

工厂模式说白了就是「把 new 这件事外包出去」。什么时候需要外包?

  • 创建逻辑复杂:比如一个对象需要根据配置、环境、参数来决定具体创建哪个子类。
  • 创建过程需要复用:多个地方都要创建同一类对象,把创建逻辑抽出来,避免重复代码。
  • 想隐藏具体实现:调用方只关心接口,不关心具体是哪个类。
// 不用工厂时,到处都是 if-else
if (dbType == "mysql") {
    conn = new MySQLConnection();
} else if (dbType == "postgres") {
    conn = new PostgresConnection();
}

// 用了工厂,调用方只需一行
conn = DatabaseFactory.createConnection(dbType);

嗯,这里要注意:不要为了用工厂而用工厂。如果只是 new 一个简单的对象,直接 new 就行了。我见过有人连 String 都要用工厂创建,那就过度设计了。

我的经验:当创建对象的代码开始出现重复的 if-else 或 switch 时,就是引入工厂的好时机。别提前优化,也别拖到代码发臭。

四、何时使用观察者

观察者模式解决的是「一件事发生了,谁想知道谁就来听」的问题。什么时候用?

  • 一对多通知:一个状态变化,多个模块需要响应。
  • 解耦发送者和接收者:发送者不需要知道谁在监听,接收者也不需要知道谁在发送。
  • 动态订阅/取消:监听关系在运行时可以变化。

举个例子:用户下单后,需要发短信、发邮件、更新库存、记录日志。如果用硬编码,下单方法里会塞满各种调用。用观察者模式,下单方法只负责发布一个「OrderCreated」事件,其他模块自己去监听。

关键点:观察者模式的核心是「事件驱动」。如果你的系统里到处都是回调、监听、通知,那观察者模式就是你的菜。

我记得有一次做实时数据看板,后端数据一更新,前端多个图表要刷新。一开始用轮询,性能很差。后来改成观察者模式,数据变化时主动推送,前端图表自动更新。效果立竿见影。

避坑指南:我曾经在观察者链路上没注意内存泄漏——监听器注册了但没注销,导致对象无法被回收。后来养成了习惯:谁注册谁注销,生命周期要匹配

五、选型决策流程

说了这么多,到底怎么选?我一般按这个思路来:

  1. 先问问题类型:是实例数量问题?创建问题?还是通信问题?
  2. 再看复杂度:简单场景用简单方案,别一上来就上模式。
  3. 最后考虑扩展性:未来会不会变?如果会,留好扩展点。
模式选型决策流程 遇到什么问题? 实例必须唯一? (单例) 创建逻辑复杂? (工厂) 一对多通知? (观察者) ✅ 使用单例 全局唯一实例 ✅ 使用工厂 解耦创建过程 ✅ 使用观察者 事件驱动通信 💡 也可以组合使用:工厂创建单例,观察者监听工厂创建的事件

六、组合使用也很常见

这三个模式不是互斥的。实际项目中,经常能看到它们配合使用:

  • 工厂 + 单例:工厂方法返回一个单例对象,比如数据库连接池工厂。
  • 观察者 + 单例:事件总线通常是单例,所有模块通过它来发布和订阅事件。
  • 工厂 + 观察者:工厂创建对象后,通知观察者「新对象已创建」。

说白了,设计模式是工具,不是教条。用得好,代码清爽;用得不好,反而添乱。

我的建议:先理解每个模式解决的核心问题,再根据实际情况选型。如果拿不准,就问问自己:「不用这个模式,代码会变差吗?」如果答案是不会,那就别用。


公众号:蓝海资料掘金营,微信deep3321