Fork me on GitHub

8/21/2010

OOD 物件導向設計原則

閱讀這篇好文的心得筆記:How I explained OOD to my wife

Object Oriented Principle (物件導向原則):抽象、繼承與多型,就像是基本的單字和文法,是建構出一篇好文章的基本工具。而 Object Oriented Design (物件導向設計) 則是站在更高一層,探討如何利用這些基本的工具,寫出一篇好文章。

為什麼需要 OOD ?

軟體設計為了解決現實生活遇到的問題,而現實就是不斷的演進和改變。所以一個被良好設計的軟體也應該要能適應這樣的改變。好的軟體具備了三個要素:1. 容易因應改變、2. 易於擴展、3. 可重用的。

要怎麼做到這三點呢,前人提出了許多設計原則,可以簡要成一個 SOLID 口訣的五大原則

S = Single Responsibility Principle 單一責任原則
O = Opened Closed Principle 開放封閉原則
L = Liscov Substitution Principle 虎父無犬子原則
I = Interface Segregation Principle 介面簡單原則
D = Dependency Inversion Principle 依賴反轉原則

以下一一說明:

1. 單一責任原則

要去修改一個 class 不應該有超過一種的理由 (也就是說,一個 class 應該只有一項責任)
如果你的 class 因為超過一種理由而需要被改變,那代表這個 class 需要進一步被細分為更小的 class。

因為每一種責任都是一種改變的可能性,當 class 擁有多重任務的時候,程式碼之間關係會變得錯綜複雜。



如上圖的例子來說,Rectangle 這個 class 做了兩件事:

1. 計算長方形的面積  2. 將長方形畫到UI上面

這意味著在 Geometry Application 中,因為要用到 Rectangle class 所以也會間接的把給 Graphic Application 用的方法或者所需要的 library 給涵蓋進來,反之亦然。再者,當Geometry Application 需要 Rectangle class 做一些改變的時候,勢必也將會影響到 Graphic Application 的運作,需要耗費時間修改確定其影響。因此將 Rectangle class 在進一步細分 Rectangle 和 RectangleUI 兩個 class 比較適當。


2. 開放封閉原則

"Open for Extension, but Closed for Modification"

簡單來說,擴展一個 class 的行為,只需要增加程式碼,而不用對現有程式碼做修改。就好像你要改變你的造型,只需要穿上不一樣的衣服,而不用改變你的身體。





上圖的 class 階層違反了開放封閉原則。當 Server 因某種需求需要該變,Client 也需要跟著改變。就像 client 瀏覽器的實作若是和 server 緊緊相關連的話,那麼臨時要換另外一種 server,client 端的 code 也需要跟著改變。





如上圖,假若兩者之間有一個抽象層 (Abstract Server),而真正的 server 實現了這個抽象 server,那麼假若 server 需要改變就不會影響到 client 端的實作。

因此系統當中的核心觀念若能抽象得當,將來在遇到改變或者擴展需求的時候就會比較容易。


3. 虎父無犬子原則

子類別應該能夠取代其父類別。換句話說,function 當中使用指向 base type 的 reference 應該能夠傳 derived type 的物件進去,而不需要任何檢查。

這不就是多型嗎?

Liskov's Substitution Principle 就是用來確保繼承使用得當的原則。





如上圖,Ostrich 是一種鳥,但是他其實不會飛,違反了這個原則。

違反了這個原則,當你將子類別傳入父類別的參考,你無法確定發生什麼奇怪的行為;其二,針對父類別成功的單元測試,在子類別身上永遠會失敗。


4. 介面簡單原則

"Clients should not be forced to depend upon interfaces that they do not use" 保持介面的簡單明瞭!

假如公開太多的方法,會讓介面變得肥大且混亂,此外,凡是當另外一個 class 要實現這個介面的時候,將也需要一同實現那些實際上並不需要的方法。因此介面的設計原則為,他們有明確的任務,容易了解且可重用。





如上圖例,IBird 介面當中若包含了 Fly 方法,但是 Ostrich 這種鳥並不會飛,因此應該在把這個特定的方法,分出一個介面如上圖。


5. 依賴反轉原則

"高階模組不應該依賴低階模組,而是兩者都應該依賴抽象"

車子是由需多元件組成,像是引擎、輪胎等等。然而這些元件都是可外掛的,當你需要更換這些元件的時候,你只需要確認這些元件和你的車子是可匹配的。

假如這些元件並不具備外掛的特性時,如果你的引擎壞掉了,而相同一模一樣的引擎也賣光了,這時可能就要修改車子來配合別的引擎,或者換一台車!

車子就好像一個高階的模組,而他依賴低階的模組像是引擎和輪子。但是,與其直接依賴引擎和輪子,車子應該要倚賴的是引擎和輪子抽象的規格,那麼只要那些符合規格的引擎和輪子都可以讓這輛車子順利啟動。






如上圖的 Car class,有兩個抽象的 interface 屬性,而非具體的型別。因此這輛車子可以接受任何實現了這個介面規格的引擎和輪子。

當我們不貫徹這個原則的話有可能發生:
1. 高階的模組需要直接使用低階的 class
2. 當低階 class 需要改變的時候,高階模組也需要相應的改變
3. 不易重用


當然,還有許多 OOD Priciple

Composition over Inheritance” : This says about favoring composition over inheritance.
“Principle of least knowledge” : This says that "the less your class knows, the better".
“The Common Closure Principle” : This says that "related classes should be package together"
“The Stable Abstractions Principle” : This says that "the more stable a class is, the more it must consist of abstract class."

......

No comments:

Post a Comment