设计模式篇2——观察者模式:以直播间送礼系统举例

在软件开发中,我们经常遇到这样的场景:一个动作发生后,需要同时触发一系列后续操作

如果你把这些操作全部写死在一个方法里,代码就会变成一团乱麻(所谓的"面条代码"),牵一发而动全身。今天,我们通过一个生动的直播间送礼 案例,来聊聊如何用观察者模式优雅地解决这个问题。


1. 场景引入:不仅是送礼那么简单

假设你正在开发一个直播 App 的核心功能------送礼。 当用户送出一个"火箭"时,后台不仅仅是扣钱那么简单,通常还需要触发以下一堆子业务:

  1. 粉丝团系统:给用户增加粉丝团经验值。

  2. 抽奖系统:判断这次送礼是否触发了抽奖资格。

  3. PK系统:如果主播正在打 PK,需要给 PK 条增加分数。

  4. 特效系统:在公屏上播放炫酷动画。

[问题] 糟糕的实现方式(紧耦合)

如果不使用设计模式,你的 GiftService 代码很可能是这样的:

Java代码实现:

java 复制代码
public void sendGift(User user, Gift gift) {
    // 1. 核心业务:扣费
    walletService.deduct(user, gift.getPrice());

    // 2. 杂七杂八的子业务(硬编码在这里)
    fanClubService.addExp(user, 10);      // 耦合了粉丝团
    lotteryService.check(user);           // 耦合了抽奖
    pkService.addScore(user, gift.getScore()); // 耦合了PK
    
    // 如果明天产品经理说要加个"成就系统",你还得来改这行代码!
}

痛点很明显:

  • 违反开闭原则:每次增加或删除子功能,都要修改 sendGift 核心代码。

  • 维护困难GiftService 变得臃肿不堪,且依赖了太多它不该关心的服务。


2. 破局之道:观察者模式

观察者模式 (Observer Pattern) 属于行为型设计模式。

通俗定义 :它定义了一种一对多的依赖关系。当一个对象(主题)的状态发生改变时,所有依赖于它的对象(观察者)都会得到通知并自动更新。

映射到直播间场景

我们可以利用观察者模式,将"送礼"看作是一个信号 ,其他系统只需要监听这个信号即可。

  • Subject (主题/被观察者) -> 直播间送礼服务 (GiftService)

    • 职责:我是大喇叭。我只负责送礼,送完之后喊一声"有人送礼啦",至于谁想知道,你们自己来我这里登记(注册)。
  • Observer (观察者) -> 粉丝团、抽奖、PK系统

    • 职责:我是听众。我订阅了送礼事件,一旦听到喇叭喊,我就赶紧干我自己的活。

3. 代码实战:解耦的艺术

让我们重构一下上面的代码。

第一步:定义观察者接口 (Observer)

所有想监听送礼事件的子系统,都必须遵守这个标准。

Java代码实现:

java 复制代码
// 观察者接口
public interface GiftObserver {
    void onGiftSent(User user, Gift gift);
}

第二步:实现具体的观察者 (ConcreteObserver)

各个子系统实现接口,编写自己的逻辑。

Java代码实现:

java 复制代码
// 粉丝团观察者
public class FanClubObserver implements GiftObserver {
    @Override
    public void onGiftSent(User user, Gift gift) {
        System.out.println("粉丝团:增加经验值 +10");
    }
}

// PK系统观察者
public class PkObserver implements GiftObserver {
    @Override
    public void onGiftSent(User user, Gift gift) {
        System.out.println("PK系统:PK条增加分数 " + gift.getScore());
    }
}

第三步:改造送礼服务 (Subject)

GiftService 不再依赖具体的子系统,而是维护一个观察者列表。

Java代码实现:

java 复制代码
public class GiftSubject {
    // 1. 维护一个观察者列表(花名册)
    private List<GiftObserver> observers = new ArrayList<>();

    // 2. 注册方法(订阅)
    public void attach(GiftObserver observer) {
        observers.add(observer);
    }

    // 3. 移除方法(取消订阅)
    public void detach(GiftObserver observer) {
        observers.remove(observer);
    }

    // 4. 通知方法(广播)
    public void notifyObservers(User user, Gift gift) {
        for (GiftObserver observer : observers) {
            observer.onGiftSent(user, gift);
        }
    }
    
    // 核心业务逻辑
    public void sendGift(User user, Gift gift) {
        // 核心逻辑:扣费
        System.out.println("钱包:扣除余额 " + gift.getPrice());
        
        // 关键点:只负责通知,不关心具体是谁在听!
        notifyObservers(user, gift);
    }
}

4. 深度解析:优缺点与注意事项

优点:为什么我们要这么做?

  1. 极度解耦GiftService 甚至不知道 PKObserver 的存在。两者只依赖于抽象接口。

  2. 扩展性强 :双十一活动来了,需要送礼掉落碎片?只需新建一个 ActivityObserver 并注册进去,核心代码一行都不用改

  3. 符合开闭原则:对扩展开放,对修改关闭。

缺点:你需要警惕的坑

  1. 同步阻塞问题 : 在 Java 的标准实现中,notifyObservers 通常是顺序执行的(同步)。

    • 如果 FanClubObserver 卡顿了 5 秒,后面的 PkObserver 就得等 5 秒,用户的界面也会卡住。

    • 解决方案 :对于非关键业务(如发通知、加经验),建议采用异步处理(放入线程池或消息队列)。

  2. 调试困难: 由于逻辑是分散的,不像面条代码那样一眼能看完,调试时可能需要在一个个观察者之间跳跃,流程比较隐晦。


5. 进阶:Spring 中的观察者模式

在实际的 Java 开发(特别是 Spring Boot)中,我们通常不需要自己手动写 Subject 和 Observer 接口,Spring 已经为我们封装好了事件发布-监听机制,这正是观察者模式的成熟落地。

  • 定义事件 :继承 ApplicationEvent (比如 GiftSentEvent)。

  • 发布者 (Subject) :使用 ApplicationEventPublisher.publishEvent()

  • 监听者 (Observer) :在方法上添加 @EventListener 注解。

这种方式比手动写 List 维护更加优雅,且天然支持解耦。


总结

观察者模式就像是生活中的订阅报纸。报社(Subject)只管印报纸并分发,它不知道也不关心是张三还是李四在读报纸。

当你发现系统中有**"牵一发而动全身""一对多联动"**的业务场景时,请果断使用观察者模式。它能让你的核心逻辑保持纯净,让复杂的业务链路变得井井有条。

相关推荐
枫叶落雨22220 小时前
ShardingSphere 介绍
java
花花鱼20 小时前
Spring Security 与 Spring MVC
java·spring·mvc
言慢行善21 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星21 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟21 小时前
操作系统之虚拟内存
java·服务器·网络
Tong Z21 小时前
常见的限流算法和实现原理
java·开发语言
凭君语未可21 小时前
Java 中的实现类是什么
java·开发语言
He少年21 小时前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
克里斯蒂亚诺更新1 天前
myeclipse的pojie
java·ide·myeclipse
迷藏4941 天前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构