天天看點

多态對一個軟體架構師的重要性

面試經常會被問到的題目之一,面向對象的三大特征是什麼?多态則是三大特征之一,個人認為三大特征中最為重要的,另外的兩大特征是封裝和繼承。

為什麼說多态對軟體架構師非常重要,對系統軟體非常重要呢?舉個例子,當軟體面向一個客戶的時候,你會發現軟體寫得很簡單,很快就能滿足其需求。随着時間的推移,軟體面向的不再是一個客戶。每個客戶提出的需求千差萬别,尤其當出現針對性的、個性化的需求。軟體的疊代、更新會變得相對困難,拓展功能變得困難。

然而,多态很好的幫助我們解決該問題。多态對源代碼的依賴關系具有很好的控制能力,這種能力讓軟體架構師可以建構出插件式架構,讓高層政策性元件與底層實作性元件分離,底層元件可以被編譯成插件,實作獨立于高層組建的開發和部署。

也就是說當我們很好的運用多态,有三大優勢:

1、控制源代碼的依賴關系;

2、元件獨立部署能力;

3、元件獨立開發能力;

Robert C. Martin 在《整潔架構之道》中很好的闡述了多态的強大性,今天我們來學習一下。

以下:

依賴反轉

我們可以想象一下在安全和便利的多态支援出現之前,軟體是什麼樣子的。下面有一個典型的調用樹的例子,main 函數調用了一些高層函數,這些高層函數又調用了一些中層函數,這些中層函數又繼續調用了一些底層函數。在這裡,源代碼層面的依賴不可避免地要跟随程式的控制流。

圖:源代碼依賴與控制流的差別

如你所見,main 函數為了調用高層函數,它就必須能夠看到這個函數所在的子產品。在 C 中,我們會通過 #include 來實作,在Java 中則通過 import 來實作。而在 C#中則用的是 using 語句。總之,每個函數的調用都必須要引用被調用放所在的子產品。

顯然,這樣做就導緻了我們在軟體架構上别無選擇。在這裡,系統行為決定了控制流,而控制流則決定了源代碼依賴關系。

當我們使用了多态,情況就不一樣了。

圖:依賴反轉

如你所見,子產品 HL1 調用 ML1 子產品中的 F() 函數,這裡的調用是通過源代碼級别的接口來實作的。當函數在程式實際運作時,接口這個概念是不存在的,HL1 會調用 ML1 的 F() 函數。

子產品 ML1 和接口 I 在源代碼上的依賴關系或者叫繼承關系,該關系的方向和控制流正好是相反的,這就是依賴反轉。這種反轉對軟體架構設計的影響非常大。

事實上,通過利用面向對象程式設計語言所提供的這種安全便利的多态實作,無論我們面對怎樣的源代碼級别的依賴關系,都可以将其反轉。

通過這種方法,軟體架構可以完全控制采用了面向對象這種程式設計方式的系統中所有的源代碼依賴關系,而不再受到系統控制流的限制。不管哪個子產品調用或者被調用,軟體架構師都可以随意更改源代碼依賴關系。

接下來我們看一個案例:典型的系統分為使用者層、業務層、資料庫。通常我們的依賴關系是使用者層 Controller 調用業務的 Service,業務層調用資料庫層 DAO 或者資源庫 Repository。

多态對一個軟體架構師的重要性

圖:資料庫和使用者界面都依賴于業務邏輯

這意味着我們讓使用者界面和資料庫都成為業務邏輯的插件。也就是說,業務邏輯子產品的源代碼不需要引入使用者界面和資料庫這兩塊。

這樣一來,業務邏輯、使用者界面以及資料庫就可以編譯成三個被獨立的元件或者部署單元(例如 jar 檔案、DLL 檔案、Gen 檔案等),這些元件或者部署單元的依賴關系與源代碼的依賴關系是一緻的,業務邏輯元件也不會依賴于使用者界面和資料庫這兩個元件。

于是,業務邏輯元件就可以獨立于使用者界面和資料庫來進行部署了,我們對使用者界面或者資料庫的修改将不會對業務邏輯産生任何影響,這些元件都可以被分别 獨立部署。

簡單來說,當某個元件的源代碼需要修改時,僅僅需要重新部署該元件,不需要更改其他元件,這就是獨立部署的能力。

如果系統中的所有元件都可以獨立部署,那它們就可以由不同的團隊并行開發。這就是所謂的獨立開發能力。

總結

看似簡單,實則大部分程式員并沒将其運用到各自到系統軟體中。前段時間微服務很火、DDD也很熱,很多思想都離不開多态。系統軟體存在的意義是解決使用者問題,使用者問題則是核心部分,也就是使用者業務邏輯。如何有效的分離系統軟體的業務邏輯,控制與業務邏輯無關的代碼,進而降低軟體複雜度是一名優秀的工程師或者架構師必經之路。