一、 引子
記得一年前,我開始陸陸續續在自己的 blog 上連載《深入淺出設計模式》。其內容無出經典巨著《設計模式》之右,僅僅偶有己見,但是它記錄了我學習、思考和講述設計模式的過程。一晃,距離寫成最后一片設計模式的文章已有 3 月余,我卻遲遲沒有對設計模式做一個總結。心想,總不能虎頭蛇尾吧,于是便有了這篇文章。
?
二、 回顧 23 種設計模式
先來回顧下這 23 種經典的設計模式吧,下圖給出了 GOF 對它們的分類:
?
???
??? 圖中從兩個緯度將 23 種設計模式劃分為六大類:創建型類模式、創建型對象模式、結構型類模式、結構型對象模式、行為型類模式、行為型對象模式。 GOF 對這 23 種模式的劃分是有一定道理的,雖然人為的類型劃分,說到底還是有些牽強,但是如 GOF 所說,它至少可以幫助記憶學習。
??? 被分為六大塊的 23 種設計模式并不是割裂開來的,很多模式的使用往往是相生相伴的,像工廠與單例,裝飾與組合等等。 GOF 給出了模式間的關系詳細描述如下圖:
?
???
??? 記得曾經不止一次有人問我:這模式和那模式感覺上一樣啊,有什么區別啊。同樣,在很多論壇上也充滿了這樣的疑問。其實這是很正常的,面向對象設計、編程所能使用的方式不外乎這幾種:繼承、組合、封裝行為、利用多態等等,所以 23 種模式中翻來覆去的使用這幾種方式,看起來當然是似曾相逢。有人曾留言給我,讓我著重表述這些模式之間的區別與類似。我當時也許諾會在最后寫一篇總結性的文章專門討論這個話題,但是現在我不打算這樣干了。
?
三、俯瞰全局、 追蹤溯源
??? 什么是設計模式? GOF 在書中如是說: 設計模式是對被用來在特定場景下解決一般設計問題的類和相互通信的對象的描述; John Vlissides 曾說過:在設計模式中,僅有的、最重要的就是不斷的反省;而我將它比作軟件開發中經驗積累出來的“公式”。
???
通看這
23
種模式,就應了
Dennis DeBruler
曾說過的一句話:計算機科學是一門相信所有問題都可以通過多一個間接層(
indirection
)來解決的科學。在前面關于具體模式的文章中,我曾經不只一次的提到“中間層”。但是直到讀到這句話,才使我跳出具體模式的束縛俯瞰全局。
??? 23 種模式似乎不再神奇,它們在解決問題上的思路是如此的相似——添加間接層。何止模式如此,正如 Dennis DeBruler 所言,我們所接觸的很多技術都是采用這種手段,如: J2EE 的分層技術、重構等等。但是在具體操作上, 23 種模式中間接層的應用可大可小,無比靈活。觀察者模式在動作觸發端與動作執行端之間加入了目標角色層,解除了兩端之間的耦合,巧妙地解決了一對多的關系;單例模式將構造方法私有化,并在使用者與構造方法之間添加一個獲得唯一實例的靜態方法,達到控制實例個數的目的。
??? 間接層應用如此廣泛,得益于它能帶來如下好處:共享邏輯(重用)、分離意圖和實現(提高靈活性)、 隔 離變化(封裝)、解耦等等。既然我們知道了間接層這么一回事,似乎我們可以不用知道設計模式也能做出像設計模式那樣好的設計來。但是要記住,間接層應用過 于泛濫,則會過猶不及,它會導致簡單問題復雜化、跳躍閱讀難以理解等問題。那如何在設計中把握使用間接層的度呢?設計模式也許是很好的范例——你畢竟是站 在了巨人的肩上。
???
再深入一層細看,在設計模式中,廣泛遵循了兩條設計原則:面向接口編程,而不是實現;優先使用組合,而不是繼承。這兩條原則帶來的好處,自然不用再說了。說到設計原則, 現在為人熟知的設計原則有很多,如: 單一職責原則( SRP )、開閉原則( OCP )、 Liskov 替換原則( LSP )、依賴倒置原則( DIP )和接口隔離原則( ISP )等等。這些原則的出現大多都早于設計模式,但同設計模式一樣,是 OOD 過程中經驗積累的結晶。它們在 23 種設計模式中都有體現,因此了解設計原則可以幫助你更好的分析和理解設計模式。
當然,這并不是說設計模式就是建立在設計原則的基礎之上的。兩者之間的關系是互相促進的。設計原則的誕生,也許會促成新的設計模式;設計模式的出現,也許會提煉出新的設計原則。
?
現在不禁要問,為什么使用設計模式。也許你的回答會是:提高設計的重用度、靈活性、可維護性等等。但是我認為更準確的回答應該是:解決系統設計中現有的問題。這便又回到了 GOF 給設計模式下的定義上了,繞了一個圈子,原來答案就這么簡單。
這也就是我不想詳細講解各種模式區別的原因了,要解決的問題不一樣就是它們之間最大的不同。如果還要詳細分析,那它們都已寫在 GOF 巨著中了——就是它們的定義、使用范圍、優缺點等等。
?
四、 活學活用
是否我們必須按照這本巨著上描述的形式來使用設計模式呢?肯定不是這樣的。數學物理公式在不同的條件下還會有不同的衍生式,何況這些在實踐中總結的經驗呢。
Martin 建 議根據問題的復雜性逐步的引入設計模式。這是個很好的建議,它避免你套用模式而帶來了過度的設計,而這些過度的設計也許直到最后都派不上用場。比如,你的 系統中現在就僅有一個適配器角色,或者各個適配器角色沒有什么共性,那么目標角色和適配器角色就可以合為一個,這樣使得設計模式更加符合你系統的特性。
不 僅如此,做到設計模式的活學活用,我認為還要做到以解決問題為中心,將設計模式融合使用,避免為了設計而模式。當然這是建立在對各種設計模式了如指掌的情 況下。比如,有一段解析字符串的對象,而在使用它之前,還要做一些參數的判斷等其他非解析操作,這時,你很快就會想起使用代理模式;但是這個代理提供的接 口可能有兩三個(滿足不同情形),而解析對象僅有一個接口,命名還不相同,這時你又想到了適配器模式。這時也許將兩者融合成你獨有的解決方案比笨拙的套用 兩個模式好的多得多。
?
五、現在看模式
GOF 說過,這 23 種模式僅僅是對一般設計問題的總結。現在許多專有領域也開始出現了大量的設計模式,至少在我最了解的企業應用這個方向是這樣的。其中有一部分模式僅僅是對 GOF 設計模式的再次包裝。我們不妨叫 GOF 的 23 種設計模式為原始設計模式。
但是遺憾的是,越來越方便的框架支持,領域模型簡化造成代碼過程化、腳本化,使得在企業應用中很難看到原始設計模式的影子(當然還是可以看到遍地的命令模式)。比如: IoC 容器將單例、工廠、原型模式包裝了起來,你現在需要做的僅僅是填寫配置文件;框架集成了觀察者、模版等等模式,你僅僅按照框架說明實現具體對象就可以了;過程化、腳本化的代碼里面更不要提什么設計模式了!更甚者在 EJB 中單例模式差點變成了反模式。
原 始的設計模式沒有用,過時了嗎?如果你不甘心僅僅做一名代碼組裝工;想對你們部門的高手設計的框架品頭論足的話,答案就是否定的。原始設計模式有很多的確 是難得一見了,但是了解它們絕對不是在浪費你的時間,它可以讓你在解決問題的時候思路更開闊一些——它的思想比它的架勢更重要。 ?????????
?
?
寫在最后(其實應該在最前面)
??? 細想自己在學習設計模式時,常常埋怨《 Java 與模式》膚淺無物,為了模式而模式;又感嘆 GOF 寫 得高度概括,苦于理解。于是便有了將自己對設計模式的認識寫下來的想法。正巧參與了部門組織的一次設計模式講座,觸發了第一篇文章的誕生。從現在來看,文 章倒是全寫成了,可內容上卻不能讓自己滿意,卻又懶得動手修改(談何容易)。“深入”二字說來容易,做到何其難,自以為這些文章的分量遠遠夠不上;倒是 “淺出”,自以為還可以沾上點邊。你可以把本系列文章看作是《 Java 與模式》的替代品,幫你叩開設計模式之門。如果你要深入研究設計模式,我勸你還是去研讀《設計模式》一書吧。