天天看點

面向對象設計原則(二)3. 裡式替換原則4. 依賴倒轉原則5. 接口分離原則

裡氏代換原則由2008年圖靈獎得主、美國第一位計算機科學女博士 教授和卡内基·梅隆大學 Jeannette Wing教授于1994年提出。其嚴格表述如下:如果對每一個類型為S的對象o1,都有類型為T的對象o2, 使得以T定義的所有程式P在所有的對象o1代換o2時,程式P的行為沒有變化,那麼類型S是類型T的子類型。這個定義 比較拗口且難以了解,是以我們一般使用它的另一個通俗版定義:

裡氏代換原則(Liskov Substitution Principle, LSP):所有引用基類(父類)的地方必須能透明地使用其子類的對象

裡氏代換原則告訴我們,在軟體中将一個基類對象替換成它的子類對象, 程式将不會産生任何的報錯和異常, 反過來則不成立, 如果一個軟體實體使用的是一個子類對象的話, 那麼它不一定能夠使用基類對象.

裡式替換原則是實作開閉原則的重要方式之一, 由于使用基類對象的地方都可以使用子類對象, 是以在程式中盡量使基類類型來對對象進行定義, 而在運作時在确定其子類類型, 用子類對象來替換父類對象

在使用裡式替換原則時需要注意以下幾個問題:

子類的所有方法必須在父類中聲明,或子類必須實作父類中聲明的所有方法。根據裡氏代換原則,為了保證 系統的擴充性,在程式中通常使用父類來進行定義,如果一個方法隻存在子類中,在父類中不提供相應的聲 明,則無法在以父類定義的對象中使用該方法。

我們在運用裡氏代換原則時,盡量把父類設計為抽象類或者接口,讓子類繼承父類或實作父接口,并實作 在父類中聲明的方法,運作時,子類執行個體替換父類執行個體,我們可以很友善地擴充系統的功能,同時無須修改原 有子類的代碼,增加新的功能可以通過增加一個新的子類來實作。裡氏代換原則是開閉原則的具體實作手段之 一。

Java語言中,在編譯階段,Java編譯器會檢查一個程式是否符合裡氏代換原則,這是一個與實作無關的、 純文法意義上的檢查,但Java編譯器的檢查是有局限的

如果說開閉原則是面向對象設計的目标的話,那麼依賴倒轉原則就是面向對象設計的主要實作機制之一,它是系統抽 象化的具體實作。依賴倒轉原則是 在1996年為“C++Reporter”所寫的專欄Engineering Notebook的第三篇,後來加入到他在2002年出版的經典著作 "Agile Software Development, Principles, Patterns, and Practices"一書中. 依賴倒轉原則定義如下:

依賴倒轉原則(Dependency Inversion Principle, DIP), 抽象不應該依賴于細節, 細節應當依賴于抽象, 換言之, 要針對接口程式設計, 而不是針對實作程式設計

依賴倒轉原則要求我們在程式代碼中傳遞參數時或在關聯關系中,盡量引用層次高的抽象層類,即使用接口和抽象類進行變量類型聲明、參數類型聲明、方法傳回類型聲明,以及資料類型的轉換等,而不要用具體類來做這些事情。為了確定該原則的應用,一個具體類應當隻實作接口或抽象類中聲明過的方法,而不要給出多餘的方法,否則将無法調用到在子類中增加的新方法。

在引入抽象層後,系統将具有很好的靈活性,在程式中盡量使用抽象層進行程式設計,而将具體類寫在配置檔案中,這樣一來,如果系統行為發生變化,隻需要對抽象層進行擴充,并修改配置檔案,而無須修改原有系統的源代碼,在不修改的情況下來擴充系統的功能,滿足開閉原則的要求

在實作依賴倒轉原則時, 我們需要針對抽象層程式設計, 而将具體類的對象通過依賴注入的方式注入到其他對象中, 依賴注入是指當一個對象要與其他對象發生依賴關系時, 通過抽象類來注入所依賴的對象, 常用的注入方式有三種, 分别是: 構造注入, 設定注入(Setter注入)和接口注入. 構造注入是指通過構造函數來傳入具體類的對象, 設定注入是指通過Setter方法來傳入具體類的對象, 而接口注入是指通過在接口中申明的業務方法來傳入具體類的對象, 這些方法在定時使用的是抽象類型, 在運作時在傳入具體類型的對象, 由子類對象來覆寫父類對象.

我們在開發的過程中, 在service層調用service層, 或者manager層的時候, 通過注解@Resource或者@Autowired注入對象的時候, 注入的就是接口, 而不是具體的實作類, 這個就是符合我們這裡提到的依賴倒轉原則.

其實我們在很多開發的過程中, 特别是在spring的開發過程中, 很多都是使用了設計原則的, 我們隻要學習完, 在我們的設計原則中對号入座, 就能掌握了

接口分離原則定義如下:

接口分離原則(Interface Segregation Principle, ISP): 使用多個專門的接口, 而不是使用單一的總接口, 即用戶端不應該依賴那些它不需要的接口.

根據接口隔離原則,當一個接口太大時,我們需要将它分割成一些更細小的接口,使用該接口的用戶端僅需知道與之 相關的方法即可。這裡的“接 口”往往有兩種不同的含義:一種是指一個類型所具有的方法特征的集合,僅僅是一種邏輯上的抽象;另外一種是指 某種語言具體的“接口”定義,有嚴格的定義和結構,比如Java語言中的interface。對于這兩種不同的含義,ISP 的表達方式以及含義都有所不同:

(1) 當把“接口”了解成一個類型所提供的所有方法特征的集合的時候,這就是一種邏輯上的概念,接口的劃分将 直接帶來類型的劃分。可以把接口了解成角色,一個接口隻能代表一個角色,每個角色都有它特定的一個接口,此 時,這個原則可以叫做“ ”。

(2) 如果把“接口”了解成狹義的特定語言的接口,那麼ISP表達的意思是指接口僅僅提供用戶端需要的行為, 用戶端不需要的行為隐藏起來, 應該為用戶端提供盡可能小的單獨接口, 而不是提供大的總接口. 在面向對象程式設計語言中,實作一個接口就需要實作該接口中定義的所有方法,是以大的總接口使用起來不一定很友善,為了使接口的職責單一,需要将大接口中的方法根據其職責不同分别放在不同的小接口中,以確定每個接口使用起來都較為方 便,并都承擔某一單一角色。接口應該盡量細化,同時接口中的方法應該盡量少,每個接口中隻包含一個用戶端(如 子子產品或業務邏輯類)所需的方法即可,這種機制也稱為“ ”,即為不同的用戶端提供寬窄不同的接口。

其實我們在開發的過程中, 嚴格遵守我們的開發規範, 一個方法不超過15行代碼, 并且給用戶端提供接口的時候, 使用單一接口, 這個就不會存在上面的問題, 如果是一個總接口, 可以分成多個接口提供.

故事淩

明天能否加個雞腿!

喜歡作者