解耦設計手法總結

天府雲創發表於2017-04-13
解耦設計手法小結

設計是一個平衡的產物,需要在各個約束條件下(組織目標,業務目標,開發流程,技術能力,學習及維護成本等)不斷地進行演進。 我們雖然不提倡做大而全的設計,但會堅持進行基礎性設計,以保證我們的設計一直在正確的方向上演進。

設計演進的過程既可以是自上而下的,也可以是自下而上的。


基本設計原則

業界普遍被接受的設計原則不再贅述。這裡特別針對基於開源專案的軟體,其總體主旋律將是:跟隨,擴充套件,貢獻,其中跟隨將是一個基本能力,反觀深度定製的方式會遭遇越來越多的尷尬。落實在設計上,其最核心的設計原則:隔離自有業務。相較於模組化的低耦合、高內聚的原則,這裡的要求會更高。

先從模組上考慮應用的層次,依次考慮:

  • 應用層
  • 開源專案本身的定製或移植機制
  • 新增的介面層
  • 新增的適配層或業務層
  • 既有的介面層
  • 既有的實現


設計本身還要保證業務的完整性,以及對效能、系統開銷、卡頓和穩定性的要求。

解耦的設計實踐


以下為關於解耦的設計方法總結,以及應用要點便於在設計時評估。
解耦是隔離變化和降低複雜度的重要手段,這裡以解耦代言隔離變化,其思想就是以分工協作代替全面控制,介面的定義大於業務邏輯的定義。
其思考路徑是:分不分?如何分?

如何分是具體形式的問題,下面詳述。分不分則取決於功能需求, 常見分離的需求有:

  • 功能強內聚
    . 這沒什麼好說的, 最常見的理由。
  • 功能的整合和轉換
    . 就是為了整合某些功能或者達到某種切換的目的,向上提供一個更為標準統計的介面。內部可能會進行一些業務邏輯處理,資料、狀態轉換之類的操作。如編譯器分出前後端也是這樣的概念。
  • 降低複雜度


介面的定義至關重要,介面本身不能繫結業務約束或者流程。整體互動上是面向無狀態的介面,而不是程式導向。過程的合理性,即業務流程則由不同的單元內部保障,再通過介面互動。

而向約束的程式設計也是在函式內進行約束的判斷,間接達到帶狀態介面。

在手法上可以概括為從巨集觀到微觀的四個層次:

  • 程式
    . 也可以是物理空間上的分離
  • 模組化/分層
  • 程式碼

    如下圖:

程式

以分程式的方法來進行協作是Unix世界的傳統,即KISS原則。Unix下有各式小工具,這些工具之間通過管道連結起來達到強大的功能。
另外以服務的方式隔離業務也很常見。如Windows中COM+的架構,甚至是HTTP Server等。
分程式的特點在於不同程式間的功能高度獨立,並行處理的情況較多,服務提供者能夠按需佈署,存在一對多的情況,或有額外的安全性考慮。

而挑戰在於效能、系統開銷,需要熟悉IPC以及共享記憶體的知識。

分庫

這是一個重要的模組化手法,主要是以動態庫和指令碼的形式, 甚至是獨立的程式提供擴充套件。其核心思想是以外掛的形式完成功能組裝,以物理分離的形式提供出來。
外掛本身實現一套標準的介面,包括:引數配置,接收輸入,狀態輸出,資料輸出等。
如Windows的核心驅動模組,Photoshop/GIMP中的影象處理功能,Matlab以及R語言中的函式庫等等,不勝列舉。
以靜態庫形式提供出來的模組,更接近於程式碼級或者分層級別的體現,無法直接達到按需佈署的能力。
分庫要求各個獨立庫的介面層比較單一,特別適用於業務邏輯強內聚的場景。同時外掛的功能將直接影響主程式的穩定性。

分層

就是將某一類功能的類和程式碼集中起來,向外提供特定介面或若干介面類,這個邏輯上的集合,就是層(layer)或者模組(module),也有叫unit或者API之類的。與分庫、分程式本質區別就在於它是一個邏輯集合,優勢在於可以更靈活的與不同模組互動,因為介面可以多樣化(支援程式碼級的互動),這也同樣是它的劣勢,有時導致它形同虛設,喪失瞭解耦的能力。

所以分層成功與否,關鍵在於介面(含介面類)的定義和控制。

常見的一些手法,如MVC, 膠合層(glue),適配層(port),WebKit和Chrome中也有應用。

程式碼

這是一個最為微觀,最為複雜的層次。但是到了這層,並不表示必然存在耦合問題。如果一些架構在設定上考慮到了擴充套件和適配的需求,在這個級別進行解耦反而最為自然。

WebKit的port方案


以Image類為例,它的一個函式與平臺相關,於是類的實現被放在了三個檔案中Image.cpp, ImageMac.mm和ImageWin.cpp中。Image.cpp中實現了公共的部分,而ImageMac.mm中實現了Mac OS版本,而ImageWin.cpp則實現了Windows版本。



另一種實現方式,如多媒體元素的播放控制元件。首先是MediaPlayer提供了一部分公共的邏輯,對於與平臺相關實現的部分,定義了一個MediaPlayerPrivateInterface,各平臺繼承自這個介面實現各自的邏輯。



當我們解決了架構上的解耦後,在模組內部引入一定的耦合度就不是問題了。可供選擇的方法就太多了。

Helper Class

Helper Class已經為一個類新增一個友類,執行一些差異化的業務。
Helper Class可以使用類似WebKit Port的機制為不同的系統提供不同的實現,也可以配合工廠模式,實現更為彈性的選擇。


分散的邏輯判斷就可以轉為函式呼叫。


關於helper的使用一直是有爭議,網上也有很多避免使用helper class的討論。 主要論調在於認為helper class是過程化的產物,思考時是考慮的是流程上的邏輯補充。


參考:  優化解耦的設計思考

         自然而然的設計

         WebKit模組化分析

 Unix設計哲學

相關文章