第2章 面向物件的設計原則(SOLID):3_依賴倒置原則(DIP)
3. 依賴倒置原則(Dependence Inversion Principle,DIP)
3.1 定義
(1)要依賴抽象,不要依賴具體的實現類。簡單的說就是對抽象(或介面)進行程式設計,不要依賴實現進行程式設計,這樣就降低了客戶與實現模組間的耦合。包含3層含義:
①高層模組不應依賴低層模組,兩者都應該依賴於抽象
②抽象不應該依賴細節
③細節應該依賴於抽象
(2)何為“高層模組”和“低層模組”
①“低層模組”:每個邏輯的實現都是原子邏輯組成,不可分割的原子邏輯就是低層模組。一般和具體實現相關。
②“高層模組”:原子邏輯再組裝就是高層模組,一般和業務邏輯相關。如客戶端。
(3)何為“倒置”
①“依賴正置”:就是類間的依賴是實實在在的實現類間的依賴,也就是面向實現程式設計,這符合人的正常思維。如我們開賓士車就是依賴賓士車,使用膝上型電腦就直接依賴膝上型電腦。
②“依賴倒置”:程式設計是對現實世界事物進行抽象,然後我們根據系統設計的需要產生了對抽象的依賴,代替了人的傳統思維中事物間的依賴,這叫“倒置”。
3.2 依賴實現程式設計存在的問題及改進
(1)Driver只能開賓士車!
【程式設計實驗】司機只能看賓士車
//面向物件設計原則:DIP依賴倒置原則 //司機只能開賓士車——依賴具體實現 #include <stdio.h> //賓士車類 class Benz { public: void run() { printf("Benz Runing...\n"); } }; //司機類 class Driver { public: //司機類不是依賴於抽象,而是依賴具體的汽車Benz, //導致司機只能開賓士,不能開其它車的尷尬! void drive(Benz& benz) { benz.run(); } }; int main() { Driver zhangSan; Benz benz; //張三開賓士車 zhangSan.drive(benz); //引數為Benz型別,張三隻會開賓士! return 0; }
(2)解決方案——引入依賴倒置!
①通過IDriver和ICar兩個介面來類間的耦合,引入依賴倒置原則
②汽車提供run方法。司機的職能就是駕駛汽車,必須實現Drive方法。當新增加汽車類時只要該汽車實現了ICar介面,司機就可以開了。
③Client是高層業務邏輯,它對低層的依賴是建立在抽象上。
【程式設計實驗】司機可開各類車
//面向物件設計原則:DIP依賴倒置原則 //司機可開任何汽車——依賴抽象/介面 #include <stdio.h> //汽車介面 class ICar { public: virtual void run() = 0; }; //賓士車類 class Benz : public ICar { public: void run(){printf("Benz runing...\n");} }; //寶馬車類 class BWM : public ICar { public: void run(){printf("BWM runing...\n");} }; //司機介面 class IDriver { public: //是司機應該會駕駛汽車 virtual void drive(ICar& car) = 0; //依賴介面 }; //司機類 class Driver : public IDriver { public: void drive(ICar& car) //實現介面 { car.run(); } }; int main() { Driver zhangSan; Benz benz; BWM bwm; //張三開賓士車 zhangSan.drive(benz); //張三開寶馬 zhangSan.drive(bwm); return 0; }
3.3 依賴的3種寫法(詳見第1章)
(1)建構函式傳遞依賴物件
(2)Setter方法傳遞賴物件
(3)通過介面宣告依賴物件(如程式設計實驗2)
3.4 最佳實踐
(1)每個類儘量都有介面或抽象類,或兩者都有,這是依賴倒置的基本要求,有了抽象才可能依賴倒置
(2)宣告變數時儘量用介面或抽象類,例項化再用具體的類
(3)任何類都不應該從具體的類派生(或者繼承自具體類時不應超過兩層)
(4)儘量不要覆蓋基類己經實現的的方法。
(5)結合里氏替換原則,對子類進行設計。以便實現類能準確的實現業務邏輯又不違反LSP原則。