2015-03-07

常用的設計原則 (SOLID)

最近在研究Design Pattern跟Refactoring,發現對SOLID這五個常用的設計原則一知半解,索性花了一些時間研究並筆記。

SRP (single responsibility principle)
SRP用來簡化類別的責任歸屬。最初看到這個原則,只知道是在設計類別時希望儘量簡化單一類別的能力,但是為什麼要這麼做卻說不出個所以然。"responsibility"的定義為"a reason for change",這是以類別有可能需要修改的觀點為出發點的考量。多種類的功能集中在同一類別會造成功能之間的耦合,當需要新增或修改某些功能,改變的結果可能會影響原本其他不變的功能,這將會增加日後維護的成本,因此利用SRP降低不同功能之間耦合程度。

OCP (open/closed principle)
OCP最大的特色就是"對擴充保持開放,對修改保持封閉"。這到底在說什麼?同樣的,以日後維護程式碼的觀點來看,最好不要為了增加新功能而修改已經完成並經過測試的類別,因為可能會改錯並且需要重新測試,維護成本較高。那要如何增加新功能?利用繼承或實作,來增加新的類別,這樣的做法就不會提高維護的成本。簡單來說就是OO的多型。

LSP (Liskov substitution principle)
LSP有點抽象不好參透,想像在設計API時,輸入參數的型別定義為類別T,但是真正使用這個API時,輸入參數的型別可以置換為T的子類別S,這樣的置換可以成立的條件為,輸入類別為S的參數不會改變原本函式的行為與目的。

ISP (interface segregation principle)
ISP提醒我們在設計類別的結構時,不要做多餘的繼承或實作,以減少模組之間的相依性。例如有一個類別Door,擁有Lock()及Open()兩個函式,當需要新增一個新的類別TimedDoor,並增加一個新函式LockTimedOut(),經過一段時間後自動上鎖。假設已經有一個計時器的類別Timer,較好的設計會是TimedDoor同時繼承或實作Door以及Timer,而不會讓TimedDoor繼承Door,Door再繼承Timer。後者的缺點讓只需使用Door功能的程式,也必須依賴Timer,縱使完全不會用到,如此增添了多餘的相依性。

DIP (dependency inversion principle)
DIP為依賴倒轉原則。一般的設計是將架構分成low-level及high-level,low-level與high-level是相對的。例如設計一個MP3 player讀取USB上的檔案來播放音樂,會有一個專門從USB讀取檔案內容的USBReader (low-level),然後有另一個處理檔案內容的MP3Processor (high-level),MP3Processor::process() --> USBReader::read(),這是非常直觀的設計,MP3Processor depends on USBReader (high-level depends on low-level)。這種設計的問題在於,如果要增加讀取音樂檔案的來源,例如想要有一個SDReader來讀取SD card上的檔案,這樣除了新增SDReader外還必須修改MP3Processor。比較好的做法是Both high-level and low-level depend on abstraction,先建立一個抽象的interface叫IStorageReader,然後USBReader及SDReader分別實作IStorageReader的方法,而MP3Processor針對IStorageReader撰碼,如此一來USBReader depends on IStorageReader, SDReader depends on IStorageReader and MP3Processor depends on IStorageReader,這就是DIP。

[1] http://www.objectmentor.com/resources/articles/srp.pdf
[2] http://www.oodesign.com
Post a Comment