模式对比与选型:单例 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」事件,其他模块自己去监听。
关键点:观察者模式的核心是「事件驱动」。如果你的系统里到处都是回调、监听、通知,那观察者模式就是你的菜。
我记得有一次做实时数据看板,后端数据一更新,前端多个图表要刷新。一开始用轮询,性能很差。后来改成观察者模式,数据变化时主动推送,前端图表自动更新。效果立竿见影。
避坑指南:我曾经在观察者链路上没注意内存泄漏——监听器注册了但没注销,导致对象无法被回收。后来养成了习惯:谁注册谁注销,生命周期要匹配。
五、选型决策流程
说了这么多,到底怎么选?我一般按这个思路来:
- 先问问题类型:是实例数量问题?创建问题?还是通信问题?
- 再看复杂度:简单场景用简单方案,别一上来就上模式。
- 最后考虑扩展性:未来会不会变?如果会,留好扩展点。
六、组合使用也很常见
这三个模式不是互斥的。实际项目中,经常能看到它们配合使用:
- 工厂 + 单例:工厂方法返回一个单例对象,比如数据库连接池工厂。
- 观察者 + 单例:事件总线通常是单例,所有模块通过它来发布和订阅事件。
- 工厂 + 观察者:工厂创建对象后,通知观察者「新对象已创建」。
说白了,设计模式是工具,不是教条。用得好,代码清爽;用得不好,反而添乱。
我的建议:先理解每个模式解决的核心问题,再根据实际情况选型。如果拿不准,就问问自己:「不用这个模式,代码会变差吗?」如果答案是不会,那就别用。
公众号:蓝海资料掘金营,微信deep3321