二、依賴倒轉原則
1.依賴倒轉原則定義
i. 高層模塊不應該依賴低層模塊,它們都應該依賴抽象。抽象不應該依賴於細節,細節應該依賴於抽象。
ii. 要針對接口編程,不要針對實現編程。
2. 依賴倒轉原則分析
a) 簡單來說,依賴倒轉原則就是指:代碼要依賴於抽象的類,而不要依賴於具體的類;要針對接口或抽象類編程,而不是針對具體類編程。
b) 實現開閉原則的關鍵是抽象化,並且從抽象化導出具體化實現,如果說開閉原則是面向對象設計的目標的話,那麼依賴倒轉原則就是面向對象設計的主要手段。
c) 依賴倒轉原則的常用實現方式之一是在代碼中使用抽象類,而將具體類放在配置文件中。
d) 類之間的耦合 1.零耦合關係 2.具體耦合關係 3.抽象耦合關係。 依賴倒轉原則要求客戶端依賴於抽象耦合,以抽象方式耦合是依賴倒轉原則的關鍵。
e) 依賴注入:
構造注入:通過構造函數注入實例變量
設值注入:通過Setter方法注入實例變量
接口注入:通過接口方法注入實例變量
3. 依賴倒轉原則實例
某系統提供一個數據轉換模塊,可以將來自不同數據源的數據轉換成多種格式,如可以轉換來自數據庫的數據(DatabaseSource)、也可以轉換來自文本文件的數據(TextSource),轉換後的格式可以是XML文件(XMLTransformer)、也可以是XLS文件(XLSTransformer
圖(一)
圖(二)
圖(一)和圖(二)分析:
圖(一)爲什麼要到圖(二)哪?因爲該系統可能需要增加新的數據源或者新的文件格式,每增加一個新的類型的數據源或者新的類型的文件格式,客戶類MainClass都需要修改源代碼,以便使用新的類,但違背了開閉原則。現使用依賴倒轉原則對其進行重構。
總結:高層模塊不應該依賴底層模塊,兩個都應該依賴與抽象;抽象不應該依賴於細節,細節應該依賴於抽象。
三、 里氏代換原則
1. 定義
i. 如果對每一個類型爲S的對象o1,都有類型爲T的對象o2,使得以T定義的所有程序P在所有的對象o1都代換成o2時,程序P的行爲沒有變化,那麼類型S是類型T的子類型。
ii. 所有引用基類(父類)的地方必須能透明地使用其子類的對象。
2.分析
i. 在軟件中如果能夠使用基類對象,那麼一定能夠使用其子類對象。把基類都替換成它的子類,程序將不會產生任何錯誤和異常,反過來則不成立,如果一個軟件實體使用的是一個子類的話,那麼它不一定能夠使用基類。
ii. 里氏代換原則是實現開閉原則的重要方式之一,由於使用基類對象的地方都可以使用子類對象,因此在程序中儘量使用基類類型來對對象進行定義,而在運行時再確定其子類類型,用子類對象來替換父類對象。
3. 實例
某系統需要實現對重要數據(如用戶密碼)的加密處理,在數據操作類(DataOperator)中需要調用加密類中定義的加密算法,系統提供了兩個不同的加密類,CipherA和CipherB,它們實現不同的加密方法,在DataOperator中可以選擇其中的一個實現加密操作。如圖所示:
圖(一)
圖(二)
圖(一)和圖(二)分析:
圖(一)爲什到圖(二)哪?因爲如果需要更換一個加密算法類或者增加並使用一個新的加密算法類,如將CipherA改爲CipherB,則需要修改客戶類Client和數據操作類DataOperator的源代碼,違背了開閉原則。現使用里氏代換原則對其進行重構,使得系統可以靈活擴展,符合開閉原則。
總結:子類型必須能夠替換掉它們的父類型。
四、單一職責原則
1. 定義
i. 一個對象應該只包含單一的職責,並且該職責被完整地封裝在一個類中。
ii. 就一個類而言,應該僅有一個引起它變化的原因。
2. 分析
i. 一個類(或者大到模塊,小到方法)承擔的職責越多,它被複用的可能性越小,而且如果一個類承擔的職責過多,就相當於將這些職責耦合在一起,當其中一個職責變化時,可能會影響其他職責的運作。
ii.類的職責主要包括兩個方面:數據職責和行爲職責,數據職責通過其屬性來體現,而行爲職責通過其方法來體現。
iii.單一職責原則是實現高內聚、低耦合的指導方針,在很多代碼重構手法中都能找到它的存在,它是最簡單但又最難運用的原則,需要設計人員發現類的不同職責並將其分離,而發現類的多重職責需要設計人員具有較強的分析設計能力和相關重構經驗。
3. 實例
i. 某基於Java的C/S系統的“登錄功能”通過如下登錄類(Login)實現:
圖(一)
圖(二)
圖一和圖二有什麼區別哪?
圖(一)功能太過於集成,嚴重違反類的單一原則。
總結:就一個類而言,應該僅有一個引起它變化的原因。