使用 ES decorators 構建一致性 API

發表於2017-04-28

使用 ES decorators 構建一致性 API

重用和一致性是程式設計中經久不衰的兩個課題。在最新的 ES Proposal 中,「decorators 語法」為此帶來了一定的便利,並且,很適合應用於大型的類庫中。


裝飾模式

提到 decorator 大家都不會陌生,即「裝飾模式」—— 我們可以在「不侵入原有程式碼」的情況下,為程式碼增加一些「額外的功能」。

所謂「額外的功能」一般都比較獨立,不和原有邏輯耦合,只是做一層包裝。你也可以把它看成「包裝模式」。形如:

這樣看來,有一些場景特別適用這個模式,比如:

  • 記錄方法的開始執行和結束執行。
  • 為運算過程提供額外的快取能力。
  • 標記方法為 deprecated。
  • 等等。

編寫一個裝飾器

如果有好多方法都想包上這種「額外的功能」,那麼我們不會一個個地去改寫,而是考慮抽出一個「裝飾器」—— 它能夠接受原方法,然後生成包裝後的方法。比如,我們想記錄所有方法的執行時間:

使用 ES decorators

如果一個系統內需要大量運用裝飾器,那麼上述的寫法可讀性還有待提高。ES decorators 解決了這個問題,這是一個新的語法(語法糖):

新的 decorator 語法 @xxx 的形式非常類似 Java Annotation,不過後者作為靜態語言,其 Annotation 的實現機制以及使用場景和 ES decorators 都有區別,這是一個題外話。事實上,ES decorators 完全借鑑自 Python 的 decorators。

同時,聰明的你應該發現,相比手寫裝飾器,新的語法中其實「該寫的東西一個都沒少」。那這個 decorators 語法有什麼意義呢?

在我看來,這種語法糖對 decorators 的「定義」和「呼叫」都做了收斂,帶來了「形式美感」。說人話,可讀性更好。

  1. 在 decorators 定義時,約束了裝飾器的輸入(固定的幾個相關引數)和輸出(返回一個 function),使所有裝飾器風格得到收斂。
  2. 在 decorators 呼叫時,以無侵入的語法「修飾」類或方法,可維護性和可讀性都提升很多。

這兩個優勢,讓我想到 ES decorators 的一個重要使用場景,便是應用於構架一致性 API。

構架一致性 API

對於多人開發的大型類庫來說,「一致性」是很重要同時也很難執行的一個課題。這裡的「一致性」包括:

  1. 各模組提供一致的標準公用功能。
  2. 公用功能的實現和呼叫方式也保持一致。
  3. 整體 API 的風格一致。

其中 1、2 兩點可以通過引入 ES decorators 機制來更好地達到。

實踐演示

先封裝好部分 decorators(可參見 @ali/universal-decorator 這個包),這裡選取兩個裝飾器:

  • @deprecated – 用於修飾類的方法,如果方法被呼叫,則在 console 中提示此方法已經過時,以便開發者轉而呼叫其他方法。
  • @moduleLevel – 這是 Rax 體系下模組類的一個靜態成員標準欄位,可取值為幾個有限的列舉,此裝飾器對此做了約束。

接下來具體地應用到庫中。

例如 @ali/universal-tracker 中,report() 方法已經遷移到了 @ali/universal-goldlog,原方法已經廢棄,則可以寫作:

然後在呼叫 report() 後則會提示:

deprecated-result

這樣,在相關的所有庫中都引入類似的裝飾器,從而保證 API 表達上的一致,並且這些公共邏輯遵循一致的實現。

另外還有一個例子,可以用來對類的欄位做約束。以大量基於 Rax 的頁面模組為例,這些模組 class 需要宣告一個靜態屬性 moduleLevel 是 app 級別還是 page 級別,以便於框架將其渲染到對應的容器中。但是靜態成員的賦值不夠清晰明朗,也不能對列舉值做約束。使用 decorators 來改寫則:

moduleLevel 這個 decorator 將為類賦上一個名為 moduleLevel 的靜態成員,並且會對傳入值作判斷,如果入參不是 'page''app',則發出警告:

module-level-result

最後,由於使用了 ES decorators 語法的程式碼,類似於一種宣告式的標記,所以更利於我們對這些程式碼作靜態分析,比如進一步的提前校驗,或是條件編譯等等。這部分更多的想法和思路,有待發掘。

引用

  1. Exploring EcmaScript Decorators

題圖:一棵被裝飾得五光十色的聖誕樹。很多涉及到 decorator 的文章動不動就拿聖誕樹來舉例子,儼然 Christmas tree 是 decorate 的固定賓語。?

相關文章