![pic]
重用和一緻性是程式設計中經久不衰的兩個課題。在最新的 ES Proposal 中,「decorators 文法」為此帶來了一定的便利,并且,很适合應用于大型的類庫中。
提到 decorator 大家都不會陌生,即「裝飾模式」—— 我們可以在「不侵入原有代碼」的情況下,為代碼增加一些「額外的功能」。
所謂「額外的功能」一般都比較獨立,不和原有邏輯耦合,隻是做一層包裝。你也可以把它看成「包裝模式」。形如:
這樣看來,有一些場景特别适用這個模式,比如:
記錄方法的開始執行和結束執行。
為運算過程提供額外的緩存能力。
标記方法為 deprecated。
等等。
如果有好多方法都想包上這種「額外的功能」,那麼我們不會一個個地去改寫,而是考慮抽出一個「裝飾器」—— 它能夠接受原方法,然後生成包裝後的方法。比如,我們想記錄所有方法的運作時間:
如果一個系統内需要大量運用裝飾器,那麼上述的寫法可讀性還有待提高。ES decorators 解決了這個問題,這是一個新的文法(文法糖):
新的 decorator 文法 <code>@xxx</code> 的形式非常類似 Java Annotation,不過後者作為靜态語言,其 Annotation 的實作機制以及使用場景和 ES decorators 都有差別,這是一個題外話。事實上,ES decorators 完全借鑒自 Python 的 decorators。
同時,聰明的你應該發現,相比手寫裝飾器,新的文法中其實「該寫的東西一個都沒少」。那這個 decorators 文法有什麼意義呢?
在我看來,這種文法糖對 decorators 的「定義」和「調用」都做了收斂,帶來了「形式美感」。說人話,可讀性更好。
在 decorators 定義時,限制了裝飾器的輸入(固定的幾個相關參數)和輸出(傳回一個 function),使所有裝飾器風格得到收斂。
在 decorators 調用時,以無侵入的文法「修飾」類或方法,可維護性和可讀性都提升很多。
這兩個優勢,讓我想到 ES decorators 的一個重要使用場景,便是應用于構架一緻性 API。
對于多人開發的大型類庫來說,「一緻性」是很重要同時也很難執行的一個課題。這裡的「一緻性」包括:
各子產品提供一緻的标準公用功能。
公用功能的實作和調用方式也保持一緻。
整體 API 的風格一緻。
其中 1、2 兩點可以通過引入 ES decorators 機制來更好地達到。
先封裝好部分 decorators(可參見 <code>@ali/universal-decorator</code> 這個包),這裡選取兩個裝飾器:
<code>@deprecated</code> - 用于修飾類的方法,如果方法被調用,則在 console 中提示此方法已經過時,以便開發者轉而調用其他方法。
<code>@moduleLevel</code> - 這是 Rax 體系下子產品類的一個靜态成員标準字段,可取值為幾個有限的枚舉,此裝飾器對此做了限制。
接下來具體地應用到庫中。
例如 <code>@ali/universal-tracker</code> 中,<code>report()</code> 方法已經遷移到了 <code>@ali/universal-goldlog</code>,原方法已經廢棄,則可以寫作:
然後在調用 <code>report()</code> 後則會提示:
![deprecated-result]
這樣,在相關的所有庫中都引入類似的裝飾器,進而保證 API 表達上的一緻,并且這些公共邏輯遵循一緻的實作。
另外還有一個例子,可以用來對類的字段做限制。以大量基于 Rax 的頁面子產品為例,這些子產品 class 需要聲明一個靜态屬性 <code>moduleLevel</code> 是 app 級别還是 page 級别,以便于架構将其渲染到對應的容器中。但是靜态成員的指派不夠清晰明朗,也不能對枚舉值做限制。使用 decorators 來改寫則:
moduleLevel 這個 decorator 将為類賦上一個名為 <code>moduleLevel</code> 的靜态成員,并且會對傳入值作判斷,如果入參不是 <code>'page'</code> 或 <code>'app'</code>,則發出警告:
![module-level-result]
最後,由于使用了 ES decorators 文法的代碼,類似于一種聲明式的标記,是以更利于我們對這些代碼作靜态分析,比如進一步的提前校驗,或是條件編譯等等。這部分更多的想法和思路,有待發掘。
<a href="https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841">Exploring EcmaScript Decorators</a>