從GOF的設計模式說起

設計模式對於做軟件者來說是必修的內功之一,尤其現在面向對象已經成爲主流的開發方法。掌握那些經過證實的面向對象的設計模式是學習和理解面向對象的絕佳途徑,每一個模式都是面向對象最經典的應用。以前算法+數據結構=程序的時代已經一去不復返了,軟件的複雜度越來越大,硬件水平的不斷提到,也使得在一般的應用中性能的已不是主要的問題,算法在普通的應用中顯得不像以前那麼重要。而如何降低軟件的複雜度,如何實現代碼甚至組件級的複用,如何構建穩定而開放的系統?這些問題在軟件理論的研究中越來越重要,而面向對象方法的提出很大程度的解決了這些問題,特別是使用面向對象的設計模式來降低軟件的複雜性,提高重用性,構建開放的系統已經成爲現實。掌握設計模式對於個人來說能夠很大程度上提高設計和編寫軟件的水平,同時爲學習各種框架打下了良好的基礎,並且可以觸類旁通。 
    學習某模式關鍵是學習這個模式它適用的場景和使用它所帶來的效果,這樣才能理解爲什麼要使用這個模式,從而更深層的理解這個模式。多與沒有使用模式前的設計相比較,從而在差別中體會模式的妙處。同時橫向對比各個模式也是很有益的,許多模式使用的場合差不多,細微的差別往往只用通過對比才能比較出來,特別是創建型模式的,他們都有類似相同的使用場景,但產生的效果不同。 
下面從實際中看看使用模式所帶來的好處: 
  當前主流的web框架他們爲什麼能夠流行,我想與良好的的設計是分不開得,而良好的設計自然離不開設計模式,spring爲什麼能夠讓EJB2.X退出j2ee的舞臺,無疑是spring使用了恰當的設計模式的結果,spring的核心ioc(依賴注入),是工廠模式的應用,只不過這個工廠很大,大到我們我們需要都可以從中取,並讓容器把這些對象的依賴關係注入到對象內部,使用ioc不僅可以減輕我們創建實例的負擔,更重要的是避免硬編碼的new,通過xml配置依賴關係,並把依賴關係注入到對象中,從而產生鬆耦合的系統。Spring對jdbc、hibernate和ibatis的集成,使用模板方法(template method)使得我們的寫代碼大大簡化。其實Spring使用最多要算策略模式,策略模式讓我們可以輕易的用一種實現方案替換掉已有的方案,Spring在每個層中都對不同框架進行了集成(web層對structs,webwork,jsf,持久層對hibernate,ibatis,JDO,JPA的集成),而每一種框架都是一種策略,使用策略模式很容易構建開放的體系架構,這就是針對接口編程而不是類編程的原因,有了接口就可以隨意添加要替換的策略。使用dao模式很容易把對數據庫的訪問代碼分離出來,而不是把對數據庫的訪問的代碼分散的業務層,便於測試,你也很容易與抽象工廠模式結合,使的你的數據庫很容易在不同的數據庫移植,而不管你的底層是使用jdbc,ibatis,hibernate還是JDO,然後使用facade模式進行封裝,提供用戶簡單的接口的同時也把數據訪問層隱藏掉。而EJB使用的是很多理論上很有價值的,但實際上並不那麼有價值模式,例如實體bean,這個思想從理論上很好,但是在性能上很差。使用分佈式對象來增強系統的可伸縮性,不管我們現在在一臺服務器還是多臺上運行,我們只要做成分佈式的,以後就可以方便移植到多臺服務器在多個JVM運行,理論上似乎很很完美的,但實際上爲了當我們不需要分佈式的時候我們去分佈式,我們犧牲的是什麼?性能,複雜性,遠離oo,爲了減少遠程調用的往返次序,我們需要把對象粒度做得很粗,這顯然遠離了oo。並且使得設計顯得不自然,EJB設計失敗的原因可能也與當時測試驅動的方法沒有流行有很大的原因,所以使得ejb完全綁定在ejb容器中,結果是測試非常困難。無侵入的框架對於一個成爲事實標準的框架或許並不那麼重要,但是如果你使得測試難於進行,沒有生產率的框架無疑會被拋棄。現在的Spring無論在思想上還是在實際使用中許多方面都要比ejb先進,雖然EJB3.0的版本對ejb作了很大的改進,但許多方面並沒有勝過spring,並且現在Spring已經被廣泛的使用,不知道會會像ip協議那樣被因爲被廣泛使用而成爲事實的工業的標準。 
像web層核心模式大多是命令模式(command),這是因爲command模式對於request驅動pull方式是天然的恰當選擇。Webwork2.x設計的爲什麼那麼優雅,是因爲恰當模式的應用並且進行了更高層的抽象,webwork2.x和structs一樣都使用command模式,但Webwork2.x是Action普通的java類,完全不依賴servlet,看起來完全不象是web程序,帶來的好處不僅可以是脫離了servlet容器而被複用,更重要的是更容易測試,其核心是使用一個valuestack,把所有的參數值放在這個stack裏,而不是使用request獲取參數值,爽吧。同時webwork強大的攔截器功能,可以輕鬆增強bean的功能,其實無論是Filter還是AOP在動態給對象增加功能這點其實基本的思想還是來自裝飾模式。AOP可以說是對oo的很好的一個補充,將各個方面分離出來,然後動態的注入切入點,使用AOP似乎是那麼的自然,而裝飾模式更多的是一種技巧,使用起來並不顯得那麼自然,像java的IO使用的裝飾模式,給我們提供動態組合各種功能,讓java的io功能非常強大,而使用起來對於初學者來說並不那麼容易,因爲這些組合需要用戶自己去做,而不理解這個模式,會覺得這麼做很不自然,甚至覺得有點怪異。模式能夠產生優雅的框架,我們只有能夠深刻理解設計模式,才能象作家有了對語言的超強駕馭能力一樣,產生優雅的作品,才能讓我們的工作真正成爲藝術而不是技術。 
    而現在真正懂得設計模式的人並不是很多(當然我也是隻知皮毛),記得我在做編譯原理課程設計時,從實驗室主頁下載了一個用java寫的SNL編譯器源碼,估計也是研究生寫吧, 
不知道作者爲什麼選擇java語言,整個代碼感覺是使用自動化工具從c語言中自動翻譯而來, 
龐大的switch,if隨處可見,由於java不支持聯合,使用大量大粒度的對象,並且許多成員許多時候根本沒有用到。本來已經寫完了詞法分析,後來決定用java重寫,要不可真枉瞎了java了這純面向對象的語言了。下面說說我使用了設計模式後,所產生的效果。所有的語法成分都定義了一個結點類,這樣的對象是細粒度,避免了空間的浪費,並且可以單獨對接點進行語法、語義檢查以及翻譯成中間代碼,並且使用訪問者模式,把對結點的操作從結點類中分類出來成一個visitor,可以很容易增加對結點的操作,你可以通過生成一個visitor子類增加打印語法樹的功能,你可以再寫一個visitor子類來增加檢查語法錯誤的功能,增加中間代碼的翻譯,你可以隨意擴充,而這一切不需要動已經寫好的代碼,這就是模式的效果,如果你在各個結點類寫這些功能,那麼你增加一個功能你就需要更改所有結點類的實現,你想想,近百個類還不累得你頭都大了。 
  各個結點類使用組合模式來組成語法樹的樹形結構,與visitor模式結合和容易進遍歷,並且統一對各個結點處理,而不需要區分是組合結點還是葉子結點。使用Builder模式來創建結點類,並最終形成語法樹,把語法樹複雜的創建過程隱藏,只需要調用一個創建方法,一切就ok了,這就是Builder模式的效果,並且你可以很容易通過替換掉已有的Builder來改變創建的過程。 
下面是使用簡單工廠(Simple Factory)模式和命令模式(command)所產生的效果: 
public ActionFactory{ 
Action []actionCache = new Action[]{ 
new Action1(),new Action2(), 
//… 
new Action106() 

  
public Action create ActionInstance(int i){ 
   return actionCache[i]; 


actionCache 是Action的緩存,每個Action 都是一個採用Command的模式的一個動作,由於這些Action都是無狀態的(沒有數據成員),性能不會受到影響,並且由於緩存作用而使 
這些實例能夠預先創建並且得到共享,當然這只是附帶的性能上好處。我在使用這個模式的主要目的是:提供一個數字與一個動作的映射。如果從SNLCompiler的背景中來說,就是從LL(1)文法的動作表中(一個非終極符和輸入token流的匹配表,表中填寫使用的匹配的文法的序號),找到對應的文法序號,並且每條文法對應一個處理動作(Action)。從而根據找到的文法序號來產生對應的Action,然後使用多態來處理不同的文法: 
Action action = factory.createActionInstance(num); 
predict(action); 
從而避免在predict中使用龐大的 switch…case的判斷。 
使用Facade模式對編譯器的各個子功能進行封裝,留給用戶的所能看到的只有幾個子功能接口,將背後複雜的實現全部隱藏,產生的效果是簡潔而清晰的界面。 
   就像一個軟件無論如何強調oo都不爲過,而模式是表現oo的最佳途徑,以上僅僅是關於設計模式的概要。沒有具體討論各個模式。以後有空把Gof的各個模式分別總結一下。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章