Spring 环境下实现策略模式的示例

背景

最近在忙一个需求,大致就是给满足特定条件的用户发营销邮件,但是用户的来源有很多方式:从 ES 查询的、从 csv 导入的、从 MongoDB 查询….. 需求很简单,但是怎么写的优雅,方便后续扩展,就存在很多门道了。

我们的项目是基于 Spring Boot 开发的,因此这篇文章也会基于 Spring Boot 作为基础框架,教你如何使用 Spring 依赖注入的特性,优雅的实现策略模式。

1. 简单粗暴

最简单粗暴直接的方式莫过于 if...else… 了,伪代码如下:

if(来源 == ES){
 // TODO: ES Query
}else if(来源 == CSV){
 // TODO: Read CSV File
}else if(来源 == MongoDB){
 // TODO: MongoDB Query
}

如果后面还需要从其他平台获取,那就在接着添加 else if...,这种方式固然简单直接,但是当后续扩展的方式越来越多,相应的if...else...也会越来越长,emmm….. 怎么说呢,黑猫白猫,能抓到老鼠的就是好猫。

2. 策略模式

在 Spring 环境下实现策略模式异常简单,毕竟 Spring 提供的依赖注入简直就是开发利器~

既然是策略模式,那么定义策略肯定是首当其冲,策略我们使用枚举实现最佳。

public enum GroupType {
  /**
   * 从 ES 查询
   */
  ES,
  /**
   * 从 MongoDB 查询
   */
  MONGODB,
  /**
   * 从 文件 读取
   */
  FILE
}

下一步,我们定义一个接口,用于抽象通用的功能。

public interface IGroupSelect {
  /**
   * 人群获取方式
   *
   * @return 人群选择枚举
   */
  GroupType type();

  /**
   * 查询满足条件的用户
   *
   * @param groupQuery 查询条件
   * @return 满足条件的用户
   */
  default List<GroupUser> queryUser(GroupQuery groupQuery) {
    checkQueryCondition(groupQuery);
    return doQuery(groupQuery);
  }

  /**
   * 事前校验查询条件
   *
   * @param groupQuery 查询条件
   * @throws IllegalArgumentException 参数异常
   */
  void checkQueryCondition(GroupQuery groupQuery) throws IllegalArgumentException;

  /**
   * 真正的查询方法
   *
   * @param groupQuery 查询条件
   * @return 满足条件的用户
   */
  List<GroupUser> doQuery(GroupQuery groupQuery);

}

这一步,小伙伴们有没有发现里面也包含了模板方法模式呢?

然后就是不同策略的具体实现了。

  • ES 策略
@Slf4j
@Service
public class EsGroupSelect implements IGroupSelect {

  /**
   * 人群获取方式
   *
   * @return 人群选择枚举
   */
  @Override
  public GroupType type() {
    return GroupType.ES;
  }

  /**
   * 事前校验查询条件
   *
   * @param groupQuery 查询条件
   * @throws IllegalArgumentException 参数异常
   */
  @Override
  public void checkQueryCondition(GroupQuery groupQuery) throws IllegalArgumentException {
    log.info("groupQuery = {}", groupQuery);
  }

  /**
   * 查询满足条件的用户
   *
   * @param groupQuery 查询条件
   * @return 满足条件的用户
   */
  @Override
  public List<GroupUser> doQuery(GroupQuery groupQuery) {
    List<GroupUser> result = new ArrayList<>();
    // TODO:
    // 1. 复杂的 ES 查询逻辑
    // 2. 根据条件筛选满足条件的用户数据
    for (int i = 1; i <= 15; i++) {
      result.add(GroupUser.of("ES用户" + i, i + "@es.com"));
    }
    return result;
  }
}
  • 文件策略
@Slf4j
@Service
public class FileGroupSelect implements IGroupSelect {
  /**
   * 人群获取方式
   *
   * @return 人群选择枚举
   */
  @Override
  public GroupType type() {
    return GroupType.FILE;
  }

  /**
   * 事前校验查询条件
   *
   * @param groupQuery 查询条件
   * @throws IllegalArgumentException 参数异常
   */
  @Override
  public void checkQueryCondition(GroupQuery groupQuery) throws IllegalArgumentException {
    log.info("groupQuery = {}", groupQuery);
  }

  /**
   * 查询满足条件的用户
   *
   * @param groupQuery 查询条件
   * @return 满足条件的用户
   */
  @Override
  public List<GroupUser> doQuery(GroupQuery groupQuery) {
    List<GroupUser> result = new ArrayList<>();
    // TODO:
    // 1. 复杂的解析、读文件
    // 2. 根据条件筛选满足条件的用户数据
    for (int i = 1; i <= 3; i++) {
      result.add(GroupUser.of("文件读取用户" + i, i + "@file.com"));
    }
    return result;
  }
}
  • MongoDB 策略
@Slf4j
@Service
public class MongoGroupSelect implements IGroupSelect {
  /**
   * 人群获取方式
   *
   * @return 人群选择枚举
   */
  @Override
  public GroupType type() {
    return GroupType.MONGODB;
  }

  /**
   * 事前校验查询条件
   *
   * @param groupQuery 查询条件
   * @throws IllegalArgumentException 参数异常
   */
  @Override
  public void checkQueryCondition(GroupQuery groupQuery) throws IllegalArgumentException {
    log.info("groupQuery = {}", groupQuery);
  }

  /**
   * 查询满足条件的用户
   *
   * @param groupQuery 查询条件
   * @return 满足条件的用户
   */
  @Override
  public List<GroupUser> doQuery(GroupQuery groupQuery) {
    List<GroupUser> result = new ArrayList<>();
    // TODO:
    // 1. 复杂的 MongoDB 查询逻辑
    // 2. 根据条件筛选满足条件的用户数据
    for (int i = 1; i <= 7; i++) {
      result.add(GroupUser.of("MongoDB用户" + i, i + "@mongo.com"));
    }
    return result;
  }
}

Spring 环境下实现策略模式的示例

扫一扫手机访问