Home [閱讀筆記][Design Pattern] Ch5.依賴倒轉原則 Dependency Inversion Principle (DIP)
Post
Cancel

[閱讀筆記][Design Pattern] Ch5.依賴倒轉原則 Dependency Inversion Principle (DIP)

依賴倒轉原則 Dependency Inversion Principle (DIP)

「依賴倒轉原則」也有翻譯成「依賴倒置原則」。

  • 高層模組不應該依賴於低層模組。兩個都應該依賴抽象。
  • 抽象不應該依賴細節,細節應該依賴於抽象。

原話解釋是「抽象不應該依賴細節,細節應該依賴於抽象」。

說白了,就是要針對「介面」程式設計,不要對「實現」程式設計。

依賴倒轉其實可以說是物件導向設計的特色,用哪種語言來編寫程式不重要,如果編寫時考慮的都是如何針對抽象程式設計,而不是針對細節程式設計,即程式中所有的依賴關係都是終止於抽象類別或者介面,那就是物件導向的設計,反之那就是傳統程序式的設計了。

「依賴倒轉」其實就是誰也不要依靠誰,除了約定的介面,大家都可以靈活自如。

為什麼叫倒轉?

比如:我們做的專案大多要存取資料庫,所以就把存取資料庫的程式碼寫成了函數,每次做新專案時就去調用這些函數。這叫做高層模組依賴低模組。

問題也就出在這裡,我們要做新專案時,發現業務邏輯的高層模組都是一樣的,但是客戶希望使用不同的資料庫或儲存方式,這時就出現了麻煩了。

我們希望能再次利用這些高層模組,但,高層模組都是與低層的存取資料庫綁死在一起,沒有辦法複用這些高層模組,這就非常糟糕了。

比如:PC裡如果CPU、記憶體、硬碟都需要依賴具體的主機板,主機板一壞,所有的靈件就都沒有用了,這顯然不合理。
反過來,如果記憶體壞了,也不應該造成其他靈件不能使用才對。

如果不管高層模組,還是低層模組,它們都依賴於抽象,具體一點就是「介面」或「抽象類別」,只要介面是穩定的,那麼任何一個更改都不用擔心其他受影響,這就使得無論高層模組還是低層模組都可以很容易地被複用。這才是最好的辦法。

Liskov替換原則(LSP)

Liskov替換原則(LSP):子類型必須能夠替換掉它們的父類型。

這就像是學繼承時要理解的概念:子類別繼承了父類別,所以子類別可以以父類別的身份出現。
也就是說,把父類別都替換成它的子類別,程式的行為沒有變化。

鳥會飛,企鵝不會飛

如果在物件導向設計時,一個是鳥類別,一個是企鵝類別,鳥可以飛,企鵝不會飛,那麼企鵝是鳥嗎?企鵝可以繼承鳥類別嗎?

當然不行,因為前提說所有鳥都能飛,而企鵝飛不了,所以,企鵝不能繼承鳥類。如果企鵝繼承於鳥類,則企鵝就會飛了。

在物件導向設計時,這意味著:子類別擁有父類別所有非private的行為和屬性。
儘管在生物學分類上,企鵝是一種鳥,但在程式設計世界裡,企鵝不能以父類別:鳥的身份出現。因為前提說所有鳥都能飛,而企鵝飛不了,所以,企鵝不能繼承鳥類。

因為有了這個原則,使得繼承複用成為了可能,只有當子類別可以替換掉父類別,軟體單位的功能不受到影響時,父類別才能真正的被複用,而子類別也能夠在父類別的基礎上增加新的行為。

動物 vs. 貓狗牛羊

比方說:貓是繼承動物類別,以動物的身份擁有:吃、喝、跑、叫等行為,可當某一天,我們需要狗、牛、羊也擁有類似的行為,由於牠們都是繼承於動物,所以除了更改實體化的地方,程式其他部分都不需要改變。

1
2
3
4
5
6
//需求的變化,只要將Cat貓更換成別的動物:狗、牛、羊等,程式其他地方不需要改變
Animal animal = new Cat(); //只要改成 new Dog();
animal.();
animal.();
animal.();
animal.();
1
2
3
高層模組 → <<interface>> 介面或抽象類別 ◃- 低層模組

※ 高層模組中,參考到介面物件(關聯關係);低層模組繼承介面(繼承關係)

因為有了「Liskov替換原則」,才使得「開放-封閉」成為了可能。
(正是由於子類型別的可替換性才使得使用父類別的模組在無需修改的情況下就可能擴展)

This post is licensed under CC BY 4.0 by the author.

[閱讀筆記][Design Pattern] Ch4. 開放封閉原則 Open-Closed Principle (OCP)

[閱讀筆記][Design Pattern] Ch6.裝飾模式 Decorator