一個擴展搜索API的優化過程


概述

API 是一個服務的門面,就像衣裝是人的形象一樣。

優雅的 API 設計,能讓業務方使用起來倍兒爽,提升開發效率,降低維護成本;糟糕的 API 設計,則讓業務方遭心,陷入混沌。

本文將展示一個擴展搜索 API 的優化過程,從中也可以學到一些東西。

現狀

找一個上游工程的擴展搜索代碼如下:

extendKeywords.add((EsCondition) ConditionFactory.in("order_tags", Arrays.asList("IS_XXX_ORDER")));
extendKeywords.add(new EsCondition("goods_title", Op.match, new Match(goodsTitle, "100%")));

啊啊,真是丑死了 ! 為什么呢 ?

  • 強制類型轉換。 讓業務方寫強制類型轉換,簡直是讓業務方來遭罪的 ! 這 API 設計簡直了 !
  • 暴漏底層細節。 讓業務方 new EsCondition ,不僅是暴漏底層細節,還很難看!
  • 不方便的傳值。 為了傳一個 in 的數值,需要寫個 Arrays.asList(e) !
  • 遍布的 EsCondition 。 由於 API 設計的很裸,導致上游工程到處彌漫着 EsCondition 的煙霧。

emmm... 其實是我設計的 API 造的孽 ! 解鈴還須系鈴人。

優化過程

工廠方法

這種硬的 new EsConditon ,完全可以通過工廠方法和方法重載來消除。此外,為了收攏擴展搜索條件的構建,可以構建一個專門的 ExtendSearchParam 。

public class ExtendSearchParam implements Serializable {
  private static final long serialVersionUID = 2824767430430079287L;

  private List<EsCondition> extendConditions = new ArrayList<>();

  public ExtendSearchParam addEq(String field, Object value) {
    extendConditions.add(new EsCondition(field, Op.eq, value));
    return this;
  }

  public ExtendSearchParam addIn(String field, Object... list) {
    extendConditions.add(new EsCondition(field, Op.in, list));
    return this;
  }

  public ExtendSearchParam addRange(String field, long gte, long lte) {
    extendConditions.add(new EsCondition(field, Op.range, new Range(gte, lte)));
    return this;
  }

  public ExtendSearchParam addMatch(String field, String query) {
    extendConditions.add(new EsCondition(field, Op.match, new Match(query, "100%")));
    return this;
  }

  public ExtendSearchParam addMatch(String field, String query, String match) {
    extendConditions.add(new EsCondition(field, Op.match, new Match(query, match)));
    return this;
  }
}

這里借鑒了 Builder 模式,能夠鏈式地構建擴展搜索條件。這樣,業務方就可以舒心地寫上:

extendSearchParam.addIn(ORDER_TAGS, "IS_XXX_ORDER").addMatch("goods_title", goodsTitle);

沒有了類型強制轉換,沒有了暴漏底層細節,沒有了不方便的傳值,還可以一直 add 下去, 世界多美好 !

攔路虎

很快,就遇到了攔路虎:

private List<EsCondition> dealOrderSourceCondition(String orderSource) {        
     List<EsCondition> result = new ArrayList();
     result.add(new EsCondition(TYPE_ENTRANCE, Op.eq, "wsc"));
     result.add(new EsCondition(TYPE_PLATFORM, Op.eq, "wx"));
     return result;
}

emmm , 寫這個方法的小伙伴也是好意,封裝一個方法來構建訂單來源擴展條件也是好意。不過這給API 重構帶來了一點點小小的障礙。

怎么重寫這一段呢 ? 第一想到的是,在 dealOrderSourceCondition 的方法里額外增加一個參數 ExtendSearchParam ,傳進去,修改它。也能達到目地。但是,—— 破壞了“不可變”原則。 不可變原則要求,盡可能避免修改入參。修改入參這種行為,很容易導致不起眼的 BUG ,如果在關鍵流程中做這個事情,有可能導致故障。有線上教訓的。

怎么辦呢 ? 又不能修改 dealOrderSourceCondition 的入參,又要把這個方法的擴展搜索條件合並到已有的擴展搜索對象中。

有一種辦法 ! 合並擴展搜索對象 ExtendSearchParam 。 這樣,ExtendSearchParam 需要支持一個合並操作:

public ExtendSearchParam merge(ExtendSearchParam extendSearchParam) {
    if (extendSearchParam != null && extendSearchParam.has()) {
      extendConditions.addAll(extendSearchParam.getUnmodifiedExtendSearch());
    }
    return extendSearchParam;
  }

這樣,將 dealOrderSourceCondition 的返回值改為 ExtendSearchParam 對象,就能使用 merge 方法來合並擴展搜索條件了。

Yeap ! 想一想,除了 合並操作,還需要支持哪些操作呢 ?API 設計需要考慮周全,可不能遇到一個問題加一個支持啊 !

輔助方法

為了與原來的 OrderSearchParam 聯合使用, 需要加一些輔助方法,比如:

public boolean has() {
    return CollectionUtils.isNotEmpty(extendConditions);
  }

  public List<EsCondition> getUnmodifiedExtendSearch() {
    return Collections.unmodifiableList(extendConditions);
  }

小結

本文講解了一個擴展搜索 API 的優化過程。好的 API 設計能提升業務方的使用體驗,降低維護成本。設計優雅的 API ,需要掌握一些技巧:工廠方法、重載方法、常用操作等。

從工作中不斷發現需要優化的地方,掌握方法和技巧去解決, 也是一種提升技能的方式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM