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,新增策略无需改老代码。”