設計模式的原則?
l 單一職責:你不希望因爲電腦內存損壞而更換CPU吧,同樣也不應該讓一個類有多種修改的理由。
l 對擴展開放,對修改封閉:你一定不希望電腦只有一個內存槽,加內存就要換主板吧,程序也應該能在不修改原先程序的情況下就能擴展功能。
l 里氏替換:如果你買的DX9顯卡不支持DX9特性,那麼這個顯卡一定沒法用。如果父類的方法在子類中沒有實現那就暈了。在程序的世界中千萬別認爲鳥都會飛,先考慮清楚將會有哪些鳥吧。
l 依賴倒置:針對接口編程,這樣即使實現有變也不需要修改外部代碼。其實,現在電腦的硬件、網絡通訊等都是符合這個原則的,比如USB接口、PCI-E接口、TCP/IP協議。
l 接口隔離:花3000買一個帶拍照、聽MP3功能的手機還是花1000買一個手機、1000買一個MP3、1000買一個數碼相機呢?買了前者的話手機動不動就要修,而且還不一定是因爲不能打電話而修,買了後面三樣的話即使修也不影響其它使用,你說買哪個?
記得看過一個例子很恰當,說是修電腦比修收音機簡單多了。電腦壞了,更換一個零件即可,原因是電腦中的各部分都是基於相對穩定的接口,而且部件各司其職,不會相互影響,電腦本身就是一個非常符合設計原則的產品。收音機的修理沒有這麼簡單了,沒有什麼部件是插件式的,會修收音機的人肯定明白其中每一個部件的原理。
小程序就好像收音機,確實可以這麼做,一共才一個人做的,即使重新做也用不了多少時間。幾十個人的大項目如果要改一個需求需要牽涉所有人來修改,那麼這個項目用不了多少時間就會因爲維護成本太大,維護後BUG太多而報廢。
比較
設計模式 |
常用程度 |
適用層次 |
引入時機 |
結構複雜度 |
Abstract Factory |
比較常用 |
應用級 |
設計時 |
比較複雜 |
Builder |
一般 |
代碼級 |
編碼時 |
一般 |
Factory Method |
很常用 |
代碼級 |
編碼時 |
簡單 |
Prototype |
不太常用 |
應用級 |
編碼時、重構時 |
比較簡單 |
Singleton |
很常用 |
代碼級、應用級 |
設計時、編碼時 |
簡單 |
Adapter |
一般 |
代碼級 |
重構時 |
一般 |
Bridge |
一般 |
代碼級 |
設計時、編碼時 |
一般 |
Composite |
比較常用 |
代碼級 |
編碼時、重構時 |
比較複雜 |
Decorator |
一般 |
代碼級 |
重構時 |
比較複雜 |
Facade |
很常用 |
應用級、構架級 |
設計時、編碼時 |
簡單 |
Flyweight |
不太常用 |
代碼級、應用級 |
設計時 |
一般 |
Proxy |
比較常用 |
應用級、構架級 |
設計時、編碼時 |
簡單 |
Chain of Resp. |
不太常用 |
應用級、構架級 |
設計時、編碼時 |
比較複雜 |
Command |
比較常用 |
應用級 |
設計時、編碼時 |
比較簡單 |
Interpreter |
不太常用 |
應用級 |
設計時 |
比較複雜 |
Iterator |
一般 |
代碼級、應用級 |
編碼時、重構時 |
比較簡單 |
Mediator |
一般 |
應用級、構架級 |
編碼時、重構時 |
一般 |
Memento |
一般 |
代碼級 |
編碼時 |
比較簡單 |
Observer |
比較常用 |
應用級、構架級 |
設計時、編碼時 |
比較簡單 |
State |
一般 |
應用級 |
設計時、編碼時 |
一般 |
Strategy |
比較常用 |
應用級 |
設計時 |
一般 |
Template Method |
很常用 |
代碼級 |
編碼時、重構時 |
簡單 |
Visitor |
一般 |
應用級 |
設計時 |
比較複雜 |
注:常用程度、適用層次、使用時機等基於自己的理解,結構複雜度基於C#語言,表格中所有內容僅供參考。
原則、變化與實現
設計模式 |
變化 |
實現 |
體現的原則 |
Abstract Factory |
產品家族的擴展 |
封裝產品族系列內容的創建 |
開閉原則 |
Builder |
對象組建的變化 |
封裝對象的組建過程 |
開閉原則 |
Factory Method |
子類的實例化 |
對象的創建工作延遲到子類 |
開閉原則 |
Prototype |
實例化的類 |
封裝對原型的拷貝 |
依賴倒置原則 |
Singleton |
唯一實例 |
封裝對象產生的個數 |
|
Adapter |
對象接口的變化 |
接口的轉換 |
|
Bridge |
對象的多維度變化 |
分離接口以及實現 |
開閉原則 |
Composite |
複雜對象接口的統一 |
統一複雜對象的接口 |
里氏代換原則 |
Decorator |
對象的組合職責 |
在穩定接口上擴展 |
開閉原則 |
Facade |
子系統的高層接口 |
封裝子系統 |
開閉原則 |
Flyweight |
系統開銷的優化 |
封裝對象的獲取 |
|
Proxy |
對象訪問的變化 |
封裝對象的訪問過程 |
里氏代換原則 |
Chain of Resp. |
對象的請求過程 |
封裝對象的責任範圍 |
|
Command |
請求的變化 |
封裝行爲對對象 |
開閉原則 |
Interpreter |
領域問題的變化 |
封裝特定領域的變化 |
|
Iterator |
對象內部集合的變化 |
封裝對象內部集合的使用 |
單一職責原則 |
Mediator |
對象交互的變化 |
封裝對象間的交互 |
開閉原則 |
Memento |
狀態的輔助保存 |
封裝對象狀態的變化 |
接口隔離原則 |
Observer |
通訊對象的變化 |
封裝對象通知 |
開閉原則 |
State |
對象狀態的變化 |
封裝與狀態相關的行爲 |
單一職責原則 |
Strategy |
算法的變化 |
封裝算法 |
里氏代換原則 |
Template Method |
算法子步驟的變化 |
封裝算法結構 |
依賴倒置原則 |
Visitor |
對象操作變化 |
封裝對象操作變化 |
開閉原則 |
學習
l 掌握設計模式的意圖以及解決的問題
l 掌握設計模式所封裝的變化點以及優缺點
l 瞭解設計模式的結構圖以及各角色的職責
l 項目中是否應用了設計模式不重要,重要的是設計模式是否正確應用
l 項目中應用的設計模式和GOF設計模式的結構是否一致不重要,重要的是是否從這個結構中得意
l 不管用了還是沒有用設計模式,如果違背了原則,就是不恰當的設計
l 沒有設計模式是萬能的,沉迷於獲得一個解決方案的話可能會導致項目結構複雜、代碼可讀性差、並且造成項目延期
結束語
l 常用的GOF 23種設計模式介紹完了,這纔是起點。
l 本系列文章並沒有結束,關注之後非GOF 23種設計模式的相關文章。
l 如果適當運用C# 2.0一些有用的特性(特別是代理、泛型以及分部類和設計模式關聯比較大)的話,傳統的設計模式有非常大的改進的餘地。在實際運用的過程中,優先考慮適用語言特性,如果不行再去考慮適用設計模式。
l 迭代器模式(在C# 2.0中實現非常簡單)、解釋器模式(應用面非常小,自己也沒有整明白)以及備忘錄模式(比較簡單,沒有什麼可說的)沒有單獨立文介紹,但在代碼包中包含了相應的例子,所有代碼點擊這裏下載。