欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring 環(huán)境下實(shí)現(xiàn)策略模式的示例

 更新時(shí)間:2020年10月12日 09:49:57   作者:CodingDiary  
這篇文章主要介紹了Spring 環(huán)境下實(shí)現(xiàn)策略模式的示例,幫助大家更好的理解和使用spring框架,感興趣的朋友可以了解下

背景

最近在忙一個(gè)需求,大致就是給滿足特定條件的用戶發(fā)營(yíng)銷(xiāo)郵件,但是用戶的來(lái)源有很多方式:從 ES 查詢的、從 csv 導(dǎo)入的、從 MongoDB 查詢….. 需求很簡(jiǎn)單,但是怎么寫(xiě)的優(yōu)雅,方便后續(xù)擴(kuò)展,就存在很多門(mén)道了。

我們的項(xiàng)目是基于 Spring Boot 開(kāi)發(fā)的,因此這篇文章也會(huì)基于 Spring Boot 作為基礎(chǔ)框架,教你如何使用 Spring 依賴(lài)注入的特性,優(yōu)雅的實(shí)現(xiàn)策略模式。

1. 簡(jiǎn)單粗暴

最簡(jiǎn)單粗暴直接的方式莫過(guò)于 if...else… 了,偽代碼如下:

if(來(lái)源 == ES){
 // TODO: ES Query
}else if(來(lái)源 == CSV){
 // TODO: Read CSV File
}else if(來(lái)源 == MongoDB){
 // TODO: MongoDB Query
}

如果后面還需要從其他平臺(tái)獲取,那就在接著添加 else if...,這種方式固然簡(jiǎn)單直接,但是當(dāng)后續(xù)擴(kuò)展的方式越來(lái)越多,相應(yīng)的if...else...也會(huì)越來(lái)越長(zhǎng),emmm….. 怎么說(shuō)呢,黑貓白貓,能抓到老鼠的就是好貓。

2. 策略模式

在 Spring 環(huán)境下實(shí)現(xiàn)策略模式異常簡(jiǎn)單,畢竟 Spring 提供的依賴(lài)注入簡(jiǎn)直就是開(kāi)發(fā)利器~

既然是策略模式,那么定義策略肯定是首當(dāng)其沖,策略我們使用枚舉實(shí)現(xiàn)最佳。

public enum GroupType {
  /**
   * 從 ES 查詢
   */
  ES,
  /**
   * 從 MongoDB 查詢
   */
  MONGODB,
  /**
   * 從 文件 讀取
   */
  FILE
}

下一步,我們定義一個(gè)接口,用于抽象通用的功能。

public interface IGroupSelect {
  /**
   * 人群獲取方式
   *
   * @return 人群選擇枚舉
   */
  GroupType type();

  /**
   * 查詢滿足條件的用戶
   *
   * @param groupQuery 查詢條件
   * @return 滿足條件的用戶
   */
  default List<GroupUser> queryUser(GroupQuery groupQuery) {
    checkQueryCondition(groupQuery);
    return doQuery(groupQuery);
  }

  /**
   * 事前校驗(yàn)查詢條件
   *
   * @param groupQuery 查詢條件
   * @throws IllegalArgumentException 參數(shù)異常
   */
  void checkQueryCondition(GroupQuery groupQuery) throws IllegalArgumentException;

  /**
   * 真正的查詢方法
   *
   * @param groupQuery 查詢條件
   * @return 滿足條件的用戶
   */
  List<GroupUser> doQuery(GroupQuery groupQuery);

}

這一步,小伙伴們有沒(méi)有發(fā)現(xiàn)里面也包含了模板方法模式呢?

然后就是不同策略的具體實(shí)現(xiàn)了。

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

  /**
   * 人群獲取方式
   *
   * @return 人群選擇枚舉
   */
  @Override
  public GroupType type() {
    return GroupType.ES;
  }

  /**
   * 事前校驗(yàn)查詢條件
   *
   * @param groupQuery 查詢條件
   * @throws IllegalArgumentException 參數(shù)異常
   */
  @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. 復(fù)雜的 ES 查詢邏輯
    // 2. 根據(jù)條件篩選滿足條件的用戶數(shù)據(jù)
    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;
  }

  /**
   * 事前校驗(yàn)查詢條件
   *
   * @param groupQuery 查詢條件
   * @throws IllegalArgumentException 參數(shù)異常
   */
  @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. 復(fù)雜的解析、讀文件
    // 2. 根據(jù)條件篩選滿足條件的用戶數(shù)據(jù)
    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;
  }

  /**
   * 事前校驗(yàn)查詢條件
   *
   * @param groupQuery 查詢條件
   * @throws IllegalArgumentException 參數(shù)異常
   */
  @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. 復(fù)雜的 MongoDB 查詢邏輯
    // 2. 根據(jù)條件篩選滿足條件的用戶數(shù)據(jù)
    for (int i = 1; i <= 7; i++) {
      result.add(GroupUser.of("MongoDB用戶" + i, i + "@mongo.com"));
    }
    return result;
  }
}

現(xiàn)在到了最后一步,就是如何通過(guò) Spring 優(yōu)雅的實(shí)現(xiàn)策略模式的選擇呢?敲黑板,考試必考!

我們通過(guò)定義一個(gè)工廠類(lèi),然后使用 Spring 的依賴(lài)注入特性,可以注入一個(gè)接口的多個(gè)實(shí)現(xiàn),這里采用 List<IGroupSelect> 的形式注入,Spring 也支持通過(guò) Map<String,IGroupSelect> 的形式注入,如果使用 Map 注入,那么 key 就是類(lèi)名,小伙伴們自己也可以測(cè)試一下~

@Service
public class GroupSelectFactory {
  @Autowired
  private List<IGroupSelect> groupSelectList;

  /**
   * 根據(jù)人群類(lèi)型選擇具體的實(shí)現(xiàn)類(lèi)
   *
   * @param type 人群類(lèi)型
   * @return 人群選擇具體實(shí)現(xiàn)類(lèi)
   */
  public IGroupSelect getGroupSelect(GroupType type) {
    Optional<IGroupSelect> groupSelectOptional = groupSelectList.stream().filter(t -> t.type() == type).findAny();
    return groupSelectOptional.orElseThrow(() -> new IllegalArgumentException("暫不支持該人群方式"));
  }
}

最后寫(xiě)個(gè)定時(shí)任務(wù)測(cè)試一下吧。

@Autowired
private GroupSelectFactory groupSelectFactory;

/**
 * 模擬定時(shí)發(fā)送營(yíng)銷(xiāo)郵件
 */
@Scheduled(cron = "0/10 * * * * ?")
public void sendEmailTask() {
  List<SendEmailTask> taskList = new ArrayList<>();
  for (GroupType groupType : GroupType.values()) {
    GroupQuery groupQuery = new GroupQuery("虛頭巴腦的 " + groupType.name() + " 查詢條件");
    taskList.add(SendEmailTask.of(groupType, groupQuery));
  }

  taskList.forEach(task -> {
    List<GroupUser> groupUsers = groupSelectFactory.getGroupSelect(task.getType()).queryUser(task.getQuery());
    log.info("groupUsers = {}", groupUsers);
  });

}

@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
static class SendEmailTask implements Serializable {
  private static final long serialVersionUID = -3461263089669779193L;
  private GroupType type;
  private GroupQuery query;
}

觀察控制臺(tái),看看日志輸出吧~

總結(jié)

  • 本文使用策略模式實(shí)現(xiàn)不同人群的查詢,后續(xù)如果要增加短信、微信、釘釘?shù)南l(fā)送,是不是也可以用策略模式實(shí)現(xiàn)呢?
  • 使用 Spring 的依賴(lài)注入特性,可以注入一個(gè)接口的多個(gè)實(shí)現(xiàn),很容易就實(shí)現(xiàn)了策略模式的選擇,這樣后續(xù)添加一種策略的時(shí)候,完全不需要改動(dòng)主要邏輯,只需添加具體實(shí)現(xiàn)即可。
  • 細(xì)心的小伙伴可以發(fā)現(xiàn),本文雖然是講策略模式,其實(shí)里面還包含了模板方法、工廠模式,多種設(shè)計(jì)模式的協(xié)同作戰(zhàn),食用味道更佳喲~

配套代碼:https://github.com/xkcoding/practice_demo/tree/master/strategy-design-pattern-in-spring

以上就是Spring 環(huán)境下實(shí)現(xiàn)策略模式的示例的詳細(xì)內(nèi)容,更多關(guān)于Spring 實(shí)現(xiàn)策略模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用Mybatis實(shí)現(xiàn)分頁(yè)效果示例

    使用Mybatis實(shí)現(xiàn)分頁(yè)效果示例

    大家好,本篇文章主要講的是使用Mybatis實(shí)現(xiàn)分頁(yè)效果示例,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • mybatis通過(guò)中間表實(shí)現(xiàn)一對(duì)多查詢功能

    mybatis通過(guò)中間表實(shí)現(xiàn)一對(duì)多查詢功能

    這篇文章主要介紹了mybatis通過(guò)中間表實(shí)現(xiàn)一對(duì)多查詢,通過(guò)一個(gè)學(xué)生的id查詢出該學(xué)生所學(xué)的所有科目,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • 淺談java實(shí)現(xiàn)mongoDB的多條件查詢

    淺談java實(shí)現(xiàn)mongoDB的多條件查詢

    這篇文章主要介紹了java實(shí)現(xiàn)mongoDB的多條件查詢,具有一定參考價(jià)值,需要的朋友可以參考下。
    2017-09-09
  • 詳解Java中的do...while循環(huán)語(yǔ)句的使用方法

    詳解Java中的do...while循環(huán)語(yǔ)句的使用方法

    這篇文章主要介紹了Java中的do...while循環(huán)語(yǔ)句的使用方法,是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • Gauva使用ListenableFuture介紹說(shuō)明

    Gauva使用ListenableFuture介紹說(shuō)明

    并發(fā)是一個(gè)困難問(wèn)題,但是通過(guò)強(qiáng)大和強(qiáng)大的抽象能夠顯著的簡(jiǎn)化工作。為了簡(jiǎn)化問(wèn)題,Gauva使用ListenableFuture擴(kuò)展了JDK的Future接口,這篇文章主要介紹了Gauva使用ListenableFuture
    2023-01-01
  • SpringBoot使用@PathVariable進(jìn)行數(shù)據(jù)校驗(yàn)的流程步驟

    SpringBoot使用@PathVariable進(jìn)行數(shù)據(jù)校驗(yàn)的流程步驟

    在SpringBoot項(xiàng)目中,我們經(jīng)常需要從 URL 中獲取參數(shù)并進(jìn)行相關(guān)的數(shù)據(jù)校驗(yàn),而@PathVariable注解就是一種非常方便的方式,可以讓我們?cè)诜椒▍?shù)中直接獲取URL中的參數(shù),并進(jìn)行數(shù)據(jù)校驗(yàn),本文將介紹如何使用@PathVariable注解進(jìn)行數(shù)據(jù)校驗(yàn)
    2023-06-06
  • java8 stream sort自定義復(fù)雜排序案例

    java8 stream sort自定義復(fù)雜排序案例

    這篇文章主要介紹了java8 stream sort自定義復(fù)雜排序案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-10-10
  • 詳解Mybatis的二級(jí)緩存配置

    詳解Mybatis的二級(jí)緩存配置

    這篇文章主要介紹了Mybatis的二級(jí)緩存配置的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Spring:@Async注解和AsyncResult與CompletableFuture使用問(wèn)題

    Spring:@Async注解和AsyncResult與CompletableFuture使用問(wèn)題

    這篇文章主要介紹了Spring:@Async注解和AsyncResult與CompletableFuture使用問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 如何使用JJWT及JWT講解和工具類(lèi)

    如何使用JJWT及JWT講解和工具類(lèi)

    關(guān)于JWT的文章網(wǎng)上已經(jīng)多如牛毛了,但是相信很多同學(xué)學(xué)的還是云里霧里,所以在我學(xué)習(xí)JWT之后盡量用最簡(jiǎn)潔的描述寫(xiě)下這篇文章用于日后復(fù)習(xí),與此同時(shí)也希望可以幫助同學(xué)們共同進(jìn)步
    2021-09-09

最新評(píng)論