前言

最近在迁接口的过程中遇到一个直播列表页关注Tab的接口,需要根据不同的直播状态对关注的主播进行展示,老接口把所有的直播状态都写到一起了,首先判断该主播的直播状态(直播中/预告中/未开播等),然后执行不同的处理逻辑。

if (input.getType() == 0) {
  // 直播中主播的处理逻辑
}else if (input.getType() == 1) {
  // 预告中主播的处理逻辑
}else if (input.getType() == 2) {
  // 未开播主播的处理逻辑
}

每种逻辑返回的字段很多导致方法很大,阅读起来体验极差,而且以后有什么活动在直播列表页展示也不方面修改。现在考虑使用策略模式来消除这些if-else

实现

对于client,我们要在其中增加一个策略处理器Map<Integer, Processor>,然后在client进行bean初始化的时候将所有策略都加载到这个ConcurrentHashMap中,这样只需向client传递一个type参数就可以从该map中找到对应的处理策略的实现类。

1.客户端client

实现InitializingBean接口会在这个bean初始化后执行afterPropertiesSet方法在里面将从ApplicationContextHelper里面获取到的bean都放到map里面去,map的key为策略实现类的业务taype,值为该对象。

/**
 * @author zhongye
 * @since 2021.08.06
 */
@Component
@Slf4j
public class MyFollowActorsActionletLogic implements InitializingBean {

  private final Map<Integer, MyFollowActorsActionletProcessor> typeAndStatementHandle = new ConcurrentHashMap<>();

  @Resource
  private ApplicationContextHelper applicationContextHelper;

  @Override
  public void afterPropertiesSet() {
    Map<String, MyFollowActorsActionletProcessor> statementHandles = 
      applicationContextHelper.getBeansOfType(MyFollowActorsActionletProcessor.class);
    for (Map.Entry<String, MyFollowActorsActionletProcessor> statementHandle : statementHandles.entrySet()) {
      typeAndStatementHandle.put(statementHandle.getValue().getType(), statementHandle.getValue());
    }
  }

  public MyFollowActorsActionletOutput assembleOutput(MyFollowActorsActionletInput input) {
    MyFollowActorsActionletOutput output = typeAndStatementHandle.get(input.getType()).process(input);

    // 处理acm,加上tId
    dealFollowListAcm(output.getData());
    // 构造结果
    output.setEnd(input.getTotal() < input.getPageSize());
    output.setPageIndex(output.isEnd() ? input.getPageIndex() : input.getPageIndex() + 1);

    return output;
  }
}

2.ApplicationContextHelper是一个实现了ApplicationContextAware接口的工具类

实现这个接口会在spring容器初始化完成之后调用setApplicationContext方法,将ApplicationContext放进去,从而可以拿到spring的容器ApplicationContext,进而使用这个容器获取我们想要的bean 。

/**
 * @author zhongye
 * @since 2021.08.06
 */
@Component
public class ApplicationContextHelper implements ApplicationContextAware {

  ApplicationContext applicationContext;

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }

  public <T> T getBean(Class<T> clazz) {
    return applicationContext.getBean(clazz);
  }

  public <T> Map<String, T> getBeansOfType(Class<T> clazz) {
    return applicationContext.getBeansOfType(clazz);
  }
}

3.策略的抽象类

/**
 * MyFollowActorsActionlet 的处理策略接口
 * 0:直播中  1:预告中 2: 未开播
 *
 * @author zhongye
 * @since 2021.08.06
 */
public interface MyFollowActorsActionletProcessor {
  /**
     * 这个类型,决定了使用哪个具体的实现类
     *
     * @return 消息类型
     */
  int getType();

  /**
     * 处理逻辑
     *
     * @param input 入参
     * @return MyFollowActorsActionletOutput
     */
  MyFollowActorsActionletOutput process(MyFollowActorsActionletInput input);
}

4.策略的实现类

/**
 * 直播列表页关注TAB-正在直播中主播处理逻辑处
 *
 * @author zhongye
 * @since 2021.08.06
 */
@Component
@Slf4j
public class MyFollowLivingActorProcessor implements MyFollowActorsActionletProcessor {
  
  @Override
  public int getType() {
    //🤔 type多的时候可以写成枚举类
    return 0;
  }

  @Override
  public MyFollowActorsActionletOutput process(MyFollowActorsActionletInput input) {

    MyFollowActorsActionletOutput output = new MyFollowActorsActionletOutput();
    output.setData(Lists.newArrayList());
    long userId = input.getLoginUserId();

    // 查询直播间信息
    List<FollowActorsRoomDTO> roomInfos = actorFollowComponent.buildRoomList(userId, input.getPageIndex(), input.getPageSize());
    List<Long> actorIds = roomInfos.stream().map(FollowActorsRoomDTO::getActorId).collect(Collectors.toList());
    // 查询主播列表
    Map<Long, UsersInfo> usersInfoMap = userInfoRemoteService.mapByUserIds(actorIds);
    // 查询陪伴天数
    Map<Long, Integer> daysMap = myFollowActorsActionletLogic.getAccompanyDaysByUser(userId, actorIds);

    // 构造数据
    for (FollowActorsRoomDTO roomInfo : roomInfos) {
      UsersInfo usersInfo = usersInfoMap.get(roomInfo.getActorId());
      if (usersInfo == null) {
        continue;
      }
      FollowActorsDataItemDTO dataItem = myFollowActorsActionletLogic.buildDataItem(
        daysMap.getOrDefault(usersInfo.getUserId(), 0), input.getType(), usersInfo);
      dataItem.setRoomInfo(roomInfo);
      output.getData().add(dataItem);
    }
    input.setTotal(roomInfos.size());

    return output;
  }
}