Skip to content

1. 定义(背)

「策略模式」把一族可互相替换的算法封装成独立类,使得客户端在运行时动态切换行为,而无需修改调用代码。

2. 弹幕系统里的真实痛点

不同房间对弹幕审核、存储、推送策略差异很大:

  • 普通直播间:先审后发,写 Redis

  • 赛事直播间:高并发,先放行后异步审核,写内存 + 本地文件

  • 付费直播间:关键消息落库,普通消息只走内存

用 if-else 会导致类爆炸、单元测试困难。

3. 策略模式落地类图(简)

复制

IDanmuStrategy  (接口)
├─ NormalDanmuStrategy   # 普房
├─ MatchDanmuStrategy    # 赛事房
└─ VipDanmuStrategy      # 付费房

4. 代码实现(Spring 方式)

① 策略接口

java

复制

java
public interface DanmuStrategy {
    // 处理一条弹幕:返回 true 表示成功落库/落缓存
    boolean handle(DanmuMsg msg);
}

② 具体策略

java

复制

java
@Component("normal")
public class NormalDanmuStrategy implements DanmuStrategy {
    @Resource
    private StringRedisTemplate redisTpl;

    @Override
    public boolean handle(DanmuMsg msg) {
        // 1. 敏感词过滤
        if (SensitiveUtil.hit(msg.getContent())) return false;
        // 2. 写 Redis
        redisTpl.opsForList().leftPush("danmu:" + msg.getRoomId(), msg.toJson());
        return true;
    }
}

@Component("match")   // 高并发房
public class MatchDanmuStrategy implements DanmuStrategy {
    private final RingBuffer<DanmuEvent> ringBuffer = DisruptorBuilder.build();
    @Override
    public boolean handle(DanmuMsg msg) {
        // 直接放内存队列,异步批量审核 & 写盘
        ringBuffer.publishEvent((e, seq) -> e.setMsg(msg));
        return true;
    }
}

③ 策略工厂(无 if-else)

java

复制

java
@Component
public class DanmuStrategyFactory implements ApplicationContextAware {
    private Map<String, DanmuStrategy> strategyMap;

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        strategyMap = ctx.getBeansOfType(DanmuStrategy.class);  // key=@Component("xxx")
    }

    public DanmuStrategy get(String roomType) {
        return strategyMap.getOrDefault(roomType, strategyMap.get("normal"));
    }
}

④ 调用端(Netty Handler)

java

复制

java
@Resource
private DanmuStrategyFactory factory;

public void channelRead(ChannelHandlerContext ctx, DanmuMsg msg) {
    DanmuStrategy strategy = factory.get(msg.getRoomType());
    boolean ok = strategy.handle(msg);
    if (ok) ctx.writeAndFlush(Ack.ok(msg.getId()));
}

5. 收益

  • 新增一种房间策略:只写一个新类 + @Component("newType")零修改原代码

  • 单元测试:可注入 MockDanmuStrategy,真正做到开闭原则(OCP)


背诵金句
“弹幕零丢失靠 生产回调+队列多副本+消费后提交
策略模式把 不同业务算法 装进独立类,通过 Spring 容器动态获取,消灭 if-else,新增策略无需改老代码。”