哪些設計模式最值得學習

最近又在首頁看到幾篇設計模式相關的學習隨筆。回想起來,這幾年在園子裏發佈的有關設計模式的隨筆都有一個共同的特點。那就是Factory和Singleton居多,如果是系列的,也往往是從這兩個模式開始的。由於能夠堅持把《設計模式》中所有模式都寫完的非常少,所以基本上也很少見到有關其它模式的隨筆。

 

這種情況也很好理解,因爲《設計模式》這本書就是按照這個順序來的。最先講述的就是Abstract Factory模式,於是它排第一也無可厚非;排第二的Builder基本不太容易見到;第三的Factory Method由於也叫“Factory”所以往往和Abstract Factory放在一起,或者乾脆就混淆了; 第四的Prototype也不是太容易見到;第五位的Singleton簡單易懂,易學易用。而再往後的模式,恐怕作者們就沒什麼耐心學下去了……這可能就是爲什麼Factory和Singleton出現頻率如此之多的原因吧。

 

《設計模式》已經出版超過15年了,到今天已經不是什麼新鮮的東西了,可以說正在由“絕招”慢慢向着“基本功”轉變着。然而,這種學習模式的方式方法卻實在令人擔憂。

 

Abstract Factory在實際中並不常見,因爲它需要你有兩套並行的繼承體系,需要對同一個抽象有多於一種的實現方式。這種複雜的系統可以說不是每個領域,每個開發人員都能遇到的。在某些特定的領域可能很常見,但是在大多數領域並不需要這麼複雜的對象創建方法。這就造成了很多人“殺雞用宰牛刀”,用複雜的方式,解決不那麼複雜的問題。後果是增加了不必要的複雜度,給系統維護增加了困難。

 

另一個模式Singleton,由於實現簡單,意圖“似乎”也很明顯。被很多人用來作爲“優化”的一種方式。通過這種方式來節省內存空間,減少對象實例。但是單一實例本身就等同於全局變量,而全局變量在幾十年前就已經被證明是“反模式”了,用另一種形態的全局變量來代替另一種形態的全局變量有什麼好處麼?問題在與,Singleton的“意圖”並不在於優化,而是在於“妥協”。Singleton的目的在於保證對象有單一的實例,這是因爲對象必須要有單一的實例,如果存在多個實例,可能會引發錯誤。也就是說,Singleton以犧牲程序的清晰和可維護性,來達到保證程序正確的目的。這跟本就和優化八竿子打不着,這完全是一種設計上的妥協,犧牲一些好處來獲取更大的好處。如果僅僅是爲了節省幾個對象實例,而非程序的正確才使用Singleton,那就是丟了西瓜揀芝麻。況且節省那幾個實例也跟本就不可能對程序的性能有太大的影響(特殊領域除外)。

 

人的時間是有限的,23個模式也不是都那麼常用,哪些模式纔是最經常用到的,纔是最值得學習的呢?

 

第一梯隊:Iterator,Observer,Template Method,Strategy

Iterator:LINQ,foreach這不都是Iterator麼。

Observer:MVC的核心,.NET中事件就是Observer。

Strategy:對同一個行爲有不同實現的時候,如果考慮將行爲的實現委託(不是.NET中的委託)給另一個類,那就用到了Strategy。通過這種方式,可以簡單的替換算法的實現類,來達到更換算法的目的。

代碼
class Foo
{
  
private IBar bar;
  
public Foo(IBar bar)
  {
    
this.bar = bar;
  }

  
public void DoSomething()
  {
    
//some code
    bar.DoWhatYouWant();
    
//some code
  }
}

class A : IBar
{
  
public void DoWhatYouWant()
  {
    
//do in A's way
  }
}
class B : IBar
{
  
public void DoWhatYouWant()
  {
    
//do in B's way
  }
}

Template Method:一個算法的同一個步驟有不同的實現,通過繼承來實現。這種方式通過創建子類來改變算法的實現和行爲。ASP.NET WebForm中Page的OnInit,OnLoad等事件,就是Template Method

代碼
class Foo
{
  
public void DoSomething()
  {
    
//some code
    DoWhatYouWant();
    
//some code
  }
  
protected abstract void DoWhatYouWant();
}

class A: Foo
{
  
protected override void DoWhatYouWant();
  {
    
//do in A's way
  }
}

class B: Foo
{
  
protected override void DoWhatYouWant();
  {
    
//do in B's way
  }
}

面向對象的一個重要特點就是多態,也就是對於同一個動作有不同的行爲。Strategry通過委託的方式,將一個算法的不同實現委託給其它類;Template Method通過繼承的方式,讓子類實現算法的可變部分,基類則處理算法的流程和不變部分。近年來組合優於繼承的觀點已經成爲主流,因此Strategy的處境頻率相對高一些,但是Template Method在解決簡單問題的時候更好用,只要注意繼承層次不要太多(<=3)就好。

 

第二梯隊:Adapter,Facade,Decorator

Adapter:當你需要使用第三方庫,但是又不想太依賴於它的API,以便於今後在需要時可以方便的切換到另一個庫的時候,你就需要在你的代碼和第三方API之間放置一個抽象層,也就需要用Adapter模式了。比如你想使用log4net,如果直接在代碼中到處引用log4net的API,將來有一天需要切換到另一個庫比如EntLib,你的改動量可就大了去了。如果在一開始就自己設計一個API,在代碼中使用自己的API,再用Adapter模式將log4net的API包裝到自己的API中,如果有一天想要切換到Entlib,只要爲EntLib在寫一個Adapter就行了。對於IoC框架也是一樣的。不過需要注意的是,如果第三方庫的API接口非常龐大,使用Adapter就會很麻煩,因爲你需要包裝太多的東西,那麼使用Adapter可能就不是一個太好的主意。或許謹慎考慮確定一個不太可能會變化的第三方庫更好一些。

Facade:基本上用於簡化API,隱藏細節,在一個系統中,高層模塊調用低層模塊時,如果低層模塊API比較複雜,而高層模塊並不需要這種複雜度,那麼加一個Facade,可以簡化高層模塊使用API的難度。

Decorator:爲一個類的行爲增加行爲,比如ASP.NET MVC中的Action Filter。

 

第三梯隊:Command,State,Composite

Command:統一接口,Undo/Redo。

State:當你的model有多種狀態,model的行爲在每種狀態下並不一樣的時候,就需要用State。如果你有多個相似的Switch,那也可能意味着需要用State來代替Switch。

Composite:ASP.NET WebForm的Page和Control就是一個例子。

 

這些模式和分類只是憑我的感覺,並沒有任何實際的數據做支持,而我的感覺也只來源於我所接觸到的領域和代碼。 希望同學們也可以提供自己接觸到的代碼中,最常見到和用到的模式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章