??? 模式一直是J2EE領域的熱門話題。兩年前,對剛開始自學java并且到處碰壁的我,一本在圖書館借的《java與模式》看的我云里霧里,盡管心里面覺的他講的很有道理,可對于沒有實際項目經驗的我,真正的理解和應用還很遠很遠。轉眼工作快一年多了了,也做了幾個不大不小的項目,盡管還只是個代碼工人水準的,但是對于模式的興趣越發濃厚,于是從書堆里找出來買了好久的GOF《設計模式》。這本書是圣經級別了,可我對C++的了解有限(雖然在學校里還是狠狠啃過好幾本大部頭,現在忘光了),而且這本圣經對初學者似乎不太友好,對模式也以羅列各條目為主,例子失當。于是買了《設計模式精解》,看china-pub的書評都說對初學者很有用,而且翻譯的人也是我很佩服的gigix。
??? 在春節期間讀完了這本書,本書詳細介紹了13種常見的設計模式,以一個實際問題引出了對面向對象新觀點和設計模式的討論,在介紹完所有的模式后,更難能可貴的是作者詳細介紹了自己對模式的使用經驗(使用共同點/變化點分析,使用分析矩陣等),整本書讀下來令人心曠神怡。
? ? 我們為什么要學習設計模式呢?總之是為了獲得可以復用和容易擴展的解決方案,建立通用術語以方便團隊內的溝通交流,另外,模式能讓你以更高的層次或者說視角去觀察問題,這樣的視角將你從過早處理細節的泥潭中解放出來。模式本身就是對如何創建優良面向對象設計策略的實現:
??? 1.針對接口編程
??? 2.優先使用對象組合,而不是類繼承
??? 3.發現并封裝變化點
??? 你可以在每一個模式的背后或者每一個優秀設計的背后看到這些原則的影子。比如abstract factory、adapter、strategy體現了針對接口編程,composite、bridge體現了優先使用組合而不是繼承等。
???
??? 作者解釋了面向對象的新觀點:
???
??????????????? 原來的觀點??????????????? 新的觀點
對象?????????? 伴隨有方法的數據??????????? 擁有責任的實體,或者說擁有特定行為的實體 ?
封裝????????? 數據隱藏 ? ? ? ? ? ? ? ? ?? 各種形式的封裝,1.數據的封裝,2.方法的封裝
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 3.父類對子類的隱藏?? 4.其他對象的封裝
繼承 ? ? ? ?? 特化和復用 ? ? ? ? ? ? ? ? ? 對象分類的一種方法
??? 這些觀點其實并不新,Martin Fowler提出了軟件開發過程中的三種視覺:概念、規格和實現,過去我們對面向對象的觀點來自于實現的角度(代碼的角度)去觀察,而新的觀點只是從概念的角度重新觀察面向對象設計。
??? 深入到具體模式的討論,記錄一些需要注意的問題:
1.Adapter與Facade模式的區別
它們都是包裝器,但是兩者也有細微的區別:
.兩個模式中,我們都有已經存在的類(或者說系統)
.Facade模式中,我們無需針對接口編程;而Adapter模式我們必須針對接口編程
.Adapter模式通常是為了保持多態,而Facade模式對此不感興趣
.動機不同,Facade模式是為了簡化接口,而Adapter模式是針對一個現存的接口編程
結論:Facade模式簡化接口,而Adapter模式將接口轉換成另一個現有的接口
2.Bridge模式的理解
Bridge模式的意圖是將抽象部分與它的實現部分分離,使它們可以獨立的變化。這里的關鍵點是需要理解“實現部分”,如果把“實現部分”看成“對象外部、被對象使用的某種東西”,此模式就很好理解了。我們將變化轉移到一個使用或者擁有變化的對象(此對象是指抽象類的對象和用來實現抽象類的派生類的對象)。當出現繼承的類爆炸情況時,也許你該考慮此模式的應用場景了。此模式的UML圖

3.Observer模式,實現自己的觀察者模式也是很簡單,不過java已經內置了對此模式的支持。java.util.Observer和java.util.Observable是此模式的一個實現版本,實際應用中你所需要做的只是實現Observer接口,和繼承Observable類
4.Decorator模式是為現有的功能動態添加附加功能的一種方法,UML圖如下

java的IO庫是典型的應用實現,java.io.InputStream和java.io.OutputStream就是圖中的Component接口,FilterInputStream繼承InputStream(也就是圖中的Decorator,裝飾器),其他的如ByteArrayInputStream、FileInputStream等直接繼承自InputStream的類就是被裝飾對象,而繼承FilterInputStream的就是各式各樣的裝飾者。
5.Strategy模式是一種定義算法家族的方法,所有的算法都做相同的工作,它們只是擁有不同的實現。當你的代碼中出現了很多switch或者if else的語句,你應該考慮此模式。Strategy模式帶來的缺點是類的數量的增加,在java中可以通過將實現類作為嵌套類放在Strategy抽象類中來解決。
6.singleton模式的實現
單線程應用:
第一種:靜態初始化
public?class?Singleton?{
????private?Singleton()?{
????}
????private?static?Singleton?instance?=?new?Singleton();
????public?static?Singleton?getInstance()?{
????????return?instance;
????}
}
第二種:lazy loading
public???class???Singleton???{????
?? private???static???Singleton???instance???=???null;??
???
?? public???static?? Singleton???getInstance()???{??
?? if???(instance==null)??
?? instance=new???Singleton();??
?? return???instance;??? }????
???
??}??
多線程環境下:在C++中安全的Double-Checked Locking模式,在java中是不安全的,詳細原因與java的內存管理模型有關,請見dreamstone的文章
《java中的模式——單態》安全的實現方法是使用同步:
?? public?class?Singleton?{??
?? ??
?? ? static?Singleton?instance;??
?? ??
?? ?? public?static?synchronized?Singleton?getInstance()?{??
?? ???? if?(instance?==?null)??
?? ?????? instance?==?new?Singleton();??
?? ???? return?instance;??
?? ?? }??
? ??
? }??
《Effective Java》中提到的另一種寫法
public?class?Singleton?{
??static?class?SingletonHolder?{
????static?Singleton?instance?=?new?Singleton();
??}
??public?static?Singleton?getInstance()?{
????return?SingletonHolder.instance;
??}
}
??? 在介紹完13個模式之后,作者提出了 一種稱為分析矩陣的方法,詳細討論不是這篇短文能說的完,有興趣請找來此書的電子版看看。簡單來講,先從問題領域中分析出所有的變化點和共同點,觀察每一種必須實現的功能并作為矩陣的行,而矩陣中的列表示特定情況中的特定實現;然后觀察行,并根據場景探討使用合適的模式;最后觀察列,從整體上考慮整個問題的模式的使用。在出現的概念的場景中添加新的概念來進行設計。
??? 最后,作者總結了面向對象的原則:
.“對象”是負有定義良好的責任的東西
.對象對自己負責
.封裝意味著
? ? ——數據隱藏
? ? ——類隱藏(藏在抽象類或者接口后面)
? ? ——實現隱藏(變化封裝為對象進行引用)
.使用共同點/變化點分析抽象出行為和數據中的變化點
.針對接口編程
.把繼承考慮為一種封裝變化的方法,而不是為現有的對象制造特殊情況
.將變化點封裝在一個類中,并使之與其他變化點相分離
.力求松耦合
.力求高內聚
.絕對細心地應用“一次并且只有一次”規則(只在一個地方實現一條規則)
? ?? 書本只是提供了模式的介紹和參照,真正的應用還是要靠自己日常工作中的觀察和體驗,我將繼續在工作中理解并貫徹這些原則。 ? ??