什麼叫做依賴倒置

前兩天小組裏面開週會,有一個議題就是大家舉例來談談對設計原則的理解(SOLID原則),第一個舉例的同學談到的就是依賴倒置原則,他的例子如下:


上面的例子左邊的類顯示的是Person類依賴了具體的工具,例如Person中有一個方法drive(Car),這樣Person就對具體的交通工具產生了依賴,如果這個時候想要使用其它的交通工具如Bike,Bus,就需要修改Person類。因此將原來的設計修改成了右邊的樣子,引入了抽象接口Transportation(交通工具),這樣Person只需要依賴交通工具即可,至於到底選擇何種工具,就交給IOC容器,進行依賴注入即可。

看完了例子我們再來看看依賴倒置的定義

高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。

高層模塊不應該依賴底層模塊

這句話是依賴倒置的核心。指的是概念的自包含,上層模塊不應該去依賴具體的某個底層提供方。在設計系統時,常常會採用分層的方式:數據訪問層,業務邏輯層,展示層等,而每一層會依賴下層的API,這樣導致一個問題就是難以替換下層的提供方。那應該如何做呢,運用概念完整性,業務邏輯層需要有自己的數據訪問規範,也就是SPI,然後數據提供層去適配這個SPI,這樣如果替換了下層的數據層之後對業務邏輯層也不會產生影響。再比如電腦的主板不會去依賴顯卡,內存,而是通過自己定義的擴展槽來實現,每個不同的硬件來適配擴展槽的標準。

前些天在內網看到一篇文章,裏面有一句話很好

我家孩子跟我姓,你家孩子跟你姓,接口誰家的跟誰姓。

這句話的說的是接口所有權的歸屬問題。例如人吃巧克力

public interface IChocolates{}

public class Oreo implements IChocolates {}

public class Dove implements IChocolates {}

public interface Person {     void eat( IChocolates chocolates ); }

上面的例子中人對巧克力產生了依賴,那人吃的行爲依賴其實跟巧克力沒有關係,在巧克力出現之前就已經存在了,因此吃的動作依賴的接口應該是人本身內部的概念,這個接口的歸屬權應該屬於人,概念應該爲可食用的(edible)。因此人對巧克力的依賴關係應該倒置爲巧克力對可食用接口的依賴。這樣倒置之後對人來說具有了更好的擴展性,不僅可以吃各種不同的巧克力,還可以吃餅乾,米飯,魚肉等等其它任何可吃的東西。

public interface IChocolates extends IEdible{}

public class Oreo implements IChocolates {}

public class Dove implements IChocolates {}

public interface Person { void eat( IEdible  edible ); }

抽象不應該依賴細節;細節應該依賴抽象

有了上面的概念之後,自然也就有了依賴抽象而非細節,因爲上層制定的是SPI(Service Provider Interface), 也就是一種通用的實現接口,由不同的實現方來提供實現。另外一般實現方也會提供自己的API接口供其他應用來使用,因此一般實現方會在自己的API上面封裝一層SPI的適配層來提供給上層使用。

同時依賴抽象而非細節也是依賴注入與IOC實現的基礎。

小結

因此,依賴倒置除了我們常常說道的依賴注入,依賴抽象之外更重要的是概念的歸屬問題,在使用API的時候要去思考將要依賴的概念到底是屬於誰的,到底應該誰依賴誰,爲接口找到真正的歸屬。

其它

我們再回頭來看看第一個例子,人對交通工具有依賴,那交通工具裏面的方法呢,應該有一個運輸的方法(transport),那方法的參數呢,運輸的是什麼呢,肯定不能是人,因爲還可以運輸貨物,交通工具對人是沒有依賴的,這裏應該是可運輸的(ITransportable), Person應該去實現該接口,這樣對交通工具就是一個完整的概念了,而且更具有擴展性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章