系統架構技能之設計模式—享元模式

作者: CallHot  來源: 博客園  發布時間: 2010-12-20 23:07  閱讀: 3362 次  推薦: 0   原文鏈接   [收藏]  

  一、上篇回顧

  通過上篇的講述,我們知道裝飾模式,特別適合對某個類型的對象,動態的增加新的職責,應用程序就像使用原來的對象一樣使用對象新增的裝飾后的功能,裝 飾模式就好像是穿了一層層的外殼,這樣的方式避免了通過繼承來為類型添加新的職責的形式可取,通過繼承的方式容易造成子類的膨脹,但是當裝飾類太多的時 候,也是個難以維護的問題,至少是在裝飾對象的時候,我們可能需要多步操作來完成對象的裝飾,這時候我們可以同上面提出的改進的方案,來完成自動配置裝飾 模式,記錄操作模式的狀態,可以進行有效的回滾操作,以完成撤銷操作。

  我們先來回顧下裝飾模式的使用場景:

      1、當我們需要為某個現有的對象,動態的增加一個新的功能或職責時,可以考慮使用裝飾模式。

      2、適應于某個對象的職責經常發生變化或者經常需要動態的增加職責,避免因為這種為了適應這樣的變化,而增加繼承子類擴展的方式,因為這種方式為 造成,子類膨脹的速度過快,難以控制。

  二、摘要

  本篇我們將會講述結構性模式中的另外一個非常有用的模式-享元模式,享元模式的特點是,復用我們內存中已存在的對象,降低系統創建對象實例的性能消耗。在.NET下的值類型和引用類型的內存分配機制,我這里就不做詳細的講解了,包括引用類型與值類型之間的裝箱和拆箱的操作,這個具體的可以參考園子里面的關于這方面的文章的討論。

  我們來給出個簡單的享元模式的應用前后的對比圖,大概我們就知道享元模式的重要作用了。

  我們這里以繪制一個有樣式的字體來說明吧,有的時候我們想繪制一個純色的文字,比如紅色,那么我們可能需要創建很多的實例,通常來說,這些實例的差別不大,這個時候,我們可以考慮復用其中創建的某個實例,而不用去new這么多相同的對象,來完成這樣的工作。我們下面以這個例子來說明,使用享元模式的前后對比的情況。

  使用享元模式前:    image

  使用享元模式后:    image

  通過上圖我們可以大概的看出享元模式的目的是什么,本篇將會從以下幾點出發,講述享元模式的應用。

  1、享元模式的特點和場景。

  2、享元模式的經典實現。

  3、享元模式的其他方案。

  4、享元模式小結。

  下面我們來看下享元模式的類圖吧:

  三、本文大綱

       a、上篇回顧。

       b、摘要。

       c、本文大綱。

       d、享元模式的特點及使用場景。

       e、享元模式的經典實現。

       f、享元模式的其他方案。

       g、享元模式使用總結。

  四、享元模式的特點及使用場景

  4.1、享元模式的特點

  享元模式的意圖是通過共享有效支持大量細粒度的對象,來提供應用程序的性能,節省系統中重復創建對象實例的性能消耗,這個怎么理解呢?其實就是以下幾點的含義:

  1、當我們系統中某個對象類型的實例較多的情況。

  2、并且要求這些實例進行分類后,發現真正有區別的分類很少的情況。

      例如我們的生活中很多的場景,我們在使用拼音輸入的法的時候,如果說我們每個字都是new一個對象實例的操作的話,那么內存中的實例就太可怕,這個時候,我們是不是可以考慮將這些重復的字體在內存中只是創建一次,而是通過復用對象的形式,來組織一些可能有多個字符重復的內容呢?也許這是一個不錯的主意,我們來看看這個示例的過程吧。

image

  4.2、享元模式的使用場景

  1、當我們發現某個類型的對象有大量的實例時,我們是否可以對這些實例進行分類,經過分類后,我們發現只有很少的類別的情況下。

  2、我們發現通過使用享元模式后能夠提高系統的性能和不會帶來更多的復雜度時。

  享元模式一般是給出本地內存資源節省的一個方案,并不適合互聯網上的分布式應用的情況,不過享元模式對于排他性的要求資源的控制,是個不錯的選擇的。

  五、享元模式的經典實現

  我們下面來根據上面的我們對輸入法中的字體來進行分析,給出相關的示例代碼:

 
字體類型的基類:
public class FontBase
{

private List<string> font = new List<string>();

private string fontName;
public FontBase(string name)
{

this.fontName = name;
}


public FontBase AddFont(string font)
{

this.font.Add(font);
return this;
}

public virtual string FontName
{

get
{
return this.fontName;
}
}
}
具體的文字類型類:

public class ChineseFont : FontBase
{

public ChineseFont()
:
base("ChineseFont")
{

base.AddFont("ChineseFont");
}
}

public class EnglishFont : FontBase
{

public EnglishFont()
:
base("EnglishFont")
{

base.AddFont("EnglishFont");
}
}
具體的創建工廠類:

public class FontFactory
{

private Dictionary<string, FontBase> fonts = new Dictionary<string, FontBase>();

public FontBase Create(string name)
{
FontBase fontBase
= fonts[name];
if (fontBase != null)
return fontBase;

fontBase
= (FontBase)Activator.CreateInstance(Type.GetType(name));

return fontBase;
}
}

  通過上面實例的講解我們知道,我們通過緩存對象類型的形式來控制對象實例的創建過程,經典的模式中沒有體現共享的狀態,比如說我們在外部可能對于享元對象來說是不共享的,內部是共享的。下面我們來看看享元模式的變種吧。

  六、享元模式的其他方案

  對于上面的經典方案帶來的問題,可能我們需要更好的考慮,我們如何應對多種對象類型,我們如何管理并共享這些對象實例,這些都是我們需要考慮的問題,經過上面的思考,我們這里可以參考我們平時開發的ORM中的連接池的思路,我們這里對享元模式提供-對象池的技術。

  我們在配置文件中控制對象池中的某個類型對象實例的數量,對象的生命周期的時間,默認初始化多少個對象實例,以提前準備,為后續的使用提供服務。

  我們這里可以設計出這個專門的對象池,我們可以提供如下的功能:

  1、根據對象類型動態的創建對象實例。

  2、根據對象池中的配置,在對象池中找到空閑的實體提供給程序調用,減少創建對象的次數。

  3、我們需要設計每個類型的緩沖池,通過把對象進行緩存,提供性能。如果對象池中的對象長期不會調用,那么我們會提供一個銷毀對象的機制。

  我們來看看對象池的設計結構吧:

image

  通過上面的幾個組件,來協調完成對象池的工作。

  這里給出相關的示例代碼:

  我們先給出配置文件配置緩沖池的配置信息:

 
<?xml version="1.0" encoding="utf-8" ?>
<Cache>
<ObjectSection name="" type=""/>
<ObjectCache>
<object name="" type="" max="" timeout="" />
<object name="" type="" max="" timeout="" />
<object name="" type="" max="" timeout="" />
<object name="" type="" max="" timeout="" />
<object name="" type="" max="" timeout="" />
<object name="" type="" max="" timeout="" />
<object name="" type="" max="" timeout="" />
<object name="" type="" max="" timeout="" />
<object name="" type="" max="" timeout="" />
</ObjectCache>
</Cache>

  我們來看看具體的工廠類:

 
public class ObjectFactory
{

public static T Build<T>() where T:class,new()
{

return new T();
}
}

  七、享元模式使用總結

  享元模式的主旨意在通過共享對象節省總體資源,不過共享對象將導致資源訪問上的一些問題,包括對象實例的銷毀,還包括線程間的共享和線程內的共享是不同的,我們知道,一般線程退出的時候,會自動釋放線程內部的申請的資源,.NET會自動通過GC來回收,但是對于線程間共享的對象也許不會自動回收,這些內容需要宿主進程來進行回收,當然可能這些我們也可以通過對象池來完成這樣的回收機制。  或者說也可以參考操作系統中的隊列的情況,通過回調函數來通知進行對象的回收等。我們在對于項目中需要大量實例對象的創建工作的時候,我們就考慮一下是不是需要享元模式的應用了,希望大家能在項目中找到合適的切入點,使用合適的模式來提高程序的適應性和健壯性。由于本人水平有限,不足或者錯誤之處,還請大家批評指出。

0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()