设计模式篇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)只管印报纸并分发,它不知道也不关心是张三还是李四在读报纸。

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

相关推荐
萤丰信息2 小时前
四大核心技术领航,智慧园区重构产业生态新范式
java·大数据·人工智能·智慧城市·智慧园区
九号铅笔芯2 小时前
社区评论系统设计
java·数据库·sql
J_liaty2 小时前
客户端负载均衡与服务端负载均衡解释与对比
java·运维·负载均衡
小码过河.2 小时前
设计模式——解释器模式
java·设计模式·解释器模式
我不是8神2 小时前
RPC与 Thread 知识点全面总结
java·开发语言·jvm
君爱学习2 小时前
MySQL - 基础增删查改
java
cyforkk2 小时前
05、Java 基础硬核复习:数组的本质与面试考点
java·开发语言·面试
2 小时前
ubuntu 通过ros-noetic获取RTK模块的nmea格式数据
java·前端·javascript
橘橙黄又青2 小时前
List和Map篇
java·开发语言·面试