序言
依賴倒置,這個概念看起來很玄乎,其實很簡單。這也是我看所有技術書的心態,在心態上戰勝這本書,那么它的內容,也就能很容易理解了。依賴倒置的英文定義如下:
High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。
英文不給力的自動跳過吧。這個原則的意思就是,代碼要依賴於抽象,而不依賴於實現。這又是什么意思呢?我有個比喻:公司需要你嗎?需要,這叫依賴。但是你覺得你所在的公司離開你就不行了嗎?顯然不是的,公司只是依賴於具有擁有技能的人。所以,這里的擁有技能的人是你和你同事的抽象,當然也包括社會上的人。那所謂的倒置是什么呢?公司如果依賴你,那就叫正置;但是公司現在依賴的是具有技能的人,是抽象集合體,這就叫倒置。所謂正置是指依賴於具體的實現,而倒置則是依賴於對具體的抽象。
正文
1. 依賴倒置有什么好處呢?
依賴倒置原則的要求是依賴於抽象,那么這個問題就相當於在問,基於抽象編程有什么好處呢?這樣問題就容易回答了。抽象是什么?抽象是將一群具有相似特征和行為的個體歸為一類。這樣,我就不用為每一個個體實現的類定制函數了。試想,加入我有一個函數名叫“殺雞”,它的參數不是抽象的雞,而是土雞,那么當我要殺飼料雞的時候,還需要將“殺雞”重載一個參數為飼料雞的函數。這樣會十分麻煩。所以,依賴倒置的好處之一是:
不必為某一具體個體定制特別的處理函數,可以避免大量的無趣的函數重載。
那還有其他好處嗎?當然有了,依賴倒置可以降低具體類之間的耦合性。這是什么意思呢?看下面一段不好的程序:
1 class QiRuiQQ{ 2 public: 3 void run(){ 4 cout<<"QiRuiQQ running..."<<endl; 5 } 6 }; 7 8 class Driver{ 9 public: 10 void drive(Benz bz){ 11 bz.run(); 12 } 13 }; 14 15 int main(){ 16 17 Driver *d = new Driver(); 18 QiRuiQQ *q = new QiRuiQQ(); 19 d->drive(*q); 20 21 delete q; 22 delete d; 23 24 return 0; 25 }
這兩個類的耦合度如何呢?可以告訴你,太高了。為什么呢?試想,假如Driver要開法拉利怎么辦?我現在有一個法拉利的類:
1 class Ferrari{ 2 public: 3 void run(){ 4 cout<<"Ferrari running..."<<endl; 5 } 6 };
那Driver只能眼睜睜的看着這樣一輛超級跑車,他卻開不走。如果要讓Driver能開法拉利,我們就需要為Drive加一個重載函數,而這函數體於QiRuiQQ的drive方法驚人的相似。那么我們怎么辦呢?這個時候就需要我們偉大的依賴倒置原則了,看符合依賴倒置的代碼吧:
1 /** 2 *車輛的抽象 3 **/ 4 class ICar{ 5 public: 6 virtual void run(){} 7 }; 8 /** 9 *司機的抽象 10 **/ 11 class IDriver{ 12 public: 13 virtual void drive(ICar car){} 14 }; 15 /** 16 *車輛的具象:奇瑞QQ 17 **/ 18 class QiRuiQQ:public ICar{ 19 public: 20 void run(){ 21 cout<<"QiRuiQQ running..."<<endl; 22 } 23 }; 24 /** 25 *車輛的具象:法拉利 26 **/ 27 class Ferrari:public ICar{ 28 public: 29 void run(){ 30 cout<<"Ferrari running..."<<endl; 31 } 32 }; 33 /** 34 *司機的具象 35 **/ 36 class Driver:public IDriver{ 37 public: 38 void drive(ICar* car){ 39 car->run(); 40 } 41 }; 42 43 int main(){ 44 45 Driver *d = new Driver(); 46 QiRuiQQ *q = new QiRuiQQ(); 47 d->drive(q); 48 49 delete q; 50 delete d; 51 system("pause"); 52 return 0; 53 }
運行結果:
QiRuiQQ running...
請按任意鍵繼續. . .
此時無論你給司機什么車,司機都會開了,只要那個車繼承了ICar。這樣就降低了兩個類別的耦合度了。所以,依賴倒置的第二個好處就出來了:
降低類間的耦合度。
2. 依賴的實現方法有哪些?
所謂的依賴實現,這個概念貌似很牛逼,其實很簡單的,就是怎么把參數傳給另一個類。作者給了三種方法,我們來看看到底是哪三種吧。
◇a. 構造函數(構造對象的時候傳入另一個對象)
◇b. setter方法(Java里有Bean的概念,C++也可以完全模仿啊!其實就是一個設置成員屬性的值的函數而已)
◇c. 接口聲明依賴對象(就是我們上面的代碼了)
總結
1. 依賴倒置原則可以避免大量的函數重載。
2. 依賴倒置原則可以降低類間的耦合度。
3. 依賴的實現方法有三種,分別是:構造函數、setter方法、接口聲明依賴對象。
注意
依賴倒置雖然給我們提供了方便之門,但是從上面的代碼也可以看出,它很容易類的規模膨脹,如果為每一個類都聲明一個接口,那就會膨脹的可怕。所以,對於我們這些技術人來說,在技術的應用上,要有拿捏,不能沒有,也不能全都是。