Dependency Inversion Principle
動(dòng)機(jī)
我們?cè)O(shè)計(jì)軟件應(yīng)用時(shí),可以將完成基礎(chǔ)操作(磁盤訪問硕盹,網(wǎng)絡(luò)協(xié)議...等)的類稱為低層類墨状,將封裝復(fù)雜邏輯(業(yè)務(wù)流程...等)的類稱為高層類。后者依賴底層類纷跛。一種實(shí)現(xiàn)這種結(jié)構(gòu)的自然方式是編寫底層類谱邪,完成后再編寫高層類。由于高層類是基于其他類來定義的驰徊,這種做法似乎合乎邏輯笤闯。不過這個(gè)設(shè)計(jì)不靈活。如果我們替換掉一個(gè)低層類棍厂,會(huì)發(fā)生什么颗味?
我們來看看經(jīng)典的復(fù)制模塊例子,復(fù)制模塊從鍵盤讀取字符牺弹,然后寫入到打印設(shè)備中浦马。Copy
類是包含代碼邏輯的高層類。KeyboardReader
和 PrinterWriter
是低層類例驹。
在糟糕的設(shè)計(jì)中捐韩,高層類會(huì)直接使用并且嚴(yán)重的依賴低層類退唠。在這種情況下鹃锈,如果我們想改變?cè)O(shè)計(jì),將輸出導(dǎo)向新的 FileWriter
類上瞧预,我們將不得不修改Copy
類屎债。(我們假設(shè)這是一個(gè)超級(jí)復(fù)雜的類仅政,其中有一堆邏輯并且真地很難測(cè)試)。
為了避免這些問題盆驹,我們可以在高層類和低層類之間引入一個(gè)抽象層圆丹。由于高層模塊包含復(fù)雜的邏輯,它們不能依賴低層模塊躯喇,所以新引入的抽象層不能基于低層模塊構(gòu)建辫封。而低層模塊應(yīng)基于抽象層創(chuàng)建。
根據(jù)此原則廉丽,設(shè)計(jì)類結(jié)構(gòu)的方式則要從高層模塊到低層模塊: 高層類 --> 抽象層 --> 低層類
目的
高層模塊不應(yīng)依賴低層模塊倦微。兩則都應(yīng)該依賴抽象。
抽象不能依賴細(xì)節(jié)正压。細(xì)節(jié)應(yīng)該依賴抽象欣福。
例子
以下是一個(gè)違背了 DIP 的例子 。我們有一個(gè)高層類 Manager
和一個(gè)底層類 Worker
焦履。 我們需要給應(yīng)用增加一個(gè)新模塊來模擬由于雇傭新的熟練工導(dǎo)致的公司組織架構(gòu)的調(diào)整拓劝。為此我們建了一個(gè)新類 SuperWorker
。
我們假設(shè) Manager
類相當(dāng)復(fù)雜嘉裤,其中包含特別復(fù)雜的邏輯≈A伲現(xiàn)在我們?yōu)榱艘脒@個(gè)新類 SuperWorker
,不得不對(duì)其進(jìn)行修改屑宠。我們看看以下幾個(gè)缺點(diǎn):
- 我們得修改
Manager
類(記住牧抵,它是個(gè)復(fù)雜類,需要花費(fèi)一翻時(shí)間和功夫來完成這些修改) - 可能會(huì)影響
Manager
中現(xiàn)有的功能 - 必須重寫單元測(cè)試
所有這些問題都需要耗費(fèi)很多時(shí)間侨把,而且可能引入新錯(cuò)誤到舊有功能里犀变。如果應(yīng)用一開始設(shè)計(jì)的時(shí)候就遵循 DIP ,結(jié)果可能就不一樣了秋柄。這通常表示获枝,我們?cè)O(shè)計(jì)管理類成一個(gè) IWorker
接口,并且 Worker
類實(shí)現(xiàn) IWorker
接口骇笔。當(dāng)需要添加一個(gè) SuperWorker
省店,我們所要做的只是讓其實(shí)現(xiàn) IWorker
接口。不需要在現(xiàn)有類中做其他改動(dòng)笨触。
// Dependency Inversion Principle -- Bad Example
class Worker{
public void work(){
// ... working 干活
}
}
class Manager{
Worker worker;
public void setWorker(Worker w){
this.worker = w;
}
public void manage(){
this.worker.work();
}
}
class SuperWorker{
public void work(){
// ... working much more 干的更多
}
}
以下是遵循 DIP 原則的代碼懦傍。新設(shè)計(jì)中,添加了一個(gè)新抽象層 IWorker
接口÷樱現(xiàn)在粗俱,上面的那些問題就被解決了(不用修改高層邏輯):
- 添加
SuperWorker
的時(shí)候不需要修改Manager
- 由于不用修改
Manager
,影響其中現(xiàn)有功能的風(fēng)險(xiǎn)降到最低 - 不用重新編寫
Manager
的單元測(cè)試
// Dependency Inversion Principle -- Good example
interface IWorker{
public void work();
}
class Worker implements IWorker{
public void work(){
// ... working
}
}
class SuperWorker implements IWorker{
public void work(){
// ... working much more
}
}
class Manager{
IWorker worker;
public void setWorker(IWorker w){
this.worker = w;
}
public void manage(){
this.worker.work();
}
}
總結(jié)
當(dāng)應(yīng)用這個(gè)原則時(shí)虚吟,高層類不再直接和低層類交互寸认,它們以接口作為抽象層签财。在這種情況下,在高層類中實(shí)例化低層類(如果需要的話)就不能通過 new
操作符來完成了偏塞。相反唱蒸,可以使用一些創(chuàng)建型設(shè)計(jì)模式,如工廠模式灸叼,抽象工廠神汹,原型模式。
模版模式就是 DIP 應(yīng)用的一個(gè)具體例子古今。
當(dāng)然慎冤,使用這個(gè)原則需要你花點(diǎn)力氣的,因?yàn)樗枰憔S護(hù)更多的類和接口沧卢∫系蹋總之,它會(huì)引入更多復(fù)雜的代碼但狭,但是更加靈活披诗。不要盲目的將此原則應(yīng)用到所有的類和模塊中。如果我們的一個(gè)類功能很可能在以后都不會(huì)變立磁,那么也就不需要應(yīng)用此原則了呈队。
上一篇: 開發(fā)-關(guān)閉原則
下一篇: 接口隔離原則
目錄: http://www.reibang.com/p/af861220a6cc