設計模式(十二)享元模式(Flyweight Pattern)

來源:互聯網
上載者:User

標籤:

一、引言

  在軟體開發過程,如果我們需要重複使用某個對象的時候,如果我們重複地使用new建立這個對象的話,這樣我們在記憶體就需要多次地去申請記憶體空間了,這樣可能會出現記憶體使用量越來越多的情況,這樣的問題是非常嚴重,然而享元模式可以解決這個問題,下面具體看看享元模式是如何去解決這個問題的。

二、享元模式的詳細介紹

  在前面說了,享元模式可以解決上面的問題了,在介紹享元模式之前,讓我們先要分析下如果去解決上面那個問題,上面的問題就是重複建立了同一個對象,如果讓我們去解決這個問題肯定會這樣想:“既然都是同一個對象,能不能只建立一個對象,然後下次需要建立這個對象的時候,讓它直接用已經建立好了的對象就好了”,也就是說——讓一個對象共用。不錯,這個也是享元模式的實現精髓所在。

2.1 定義

介紹完享元模式的精髓之後,讓我們具體看看享元模式的正式定義:

  享元模式——運用共用技術有效地支援大量細粒度的對象。享元模式可以避免大量相似類的開銷,在軟體開發中如果需要產生大量細粒度的類執行個體來表示資料,如果這些執行個體除了幾個參數外基本上都是相同的,這時候就可以使用享元模式來大幅度減少需要執行個體化類的數量。如果能把這些參數(指的這些類執行個體不同的參數)移動類執行個體外面,在方法調用時將他們傳遞進來,這樣就可以通過共用大幅度地減少單個執行個體的數目。(這個也是享元模式的實現要領),然而我們把類執行個體外面的參數稱為享元對象的外部狀態,把在享元對象內部定義稱為內部狀態。

  具體享元對象的內部狀態與外部狀態的定義為:

內部狀態:

  在享元對象的內部並且不會隨著環境的改變而改變的共用部分

外部狀態:

  隨環境改變而改變的,不可以共用的狀態。

2.2 享元模式實現


  分析完享元模式的實現思路之後,相信大家實現享元模式肯定沒什麼問題了,下面以一個世紀的應用來實現下享元模式。這個例子是:一個文字編輯器中會出現很多字面,使用享元模式去實現這個文字編輯器的話,會把每個字面做成一個享元對象。享元對象的內部狀態就是這個字面,而字母在文本中的位置和字型風格等其他資訊就是它的外部狀態。下面就以這個例子來實現下享元模式,具體實現代碼如下:

    /// <summary>    /// 用戶端調用    /// </summary>    class Client    {        static void Main(string[] args)        {            // 定義外部狀態,例如字母的位置等資訊            int externalstate = 10;            // 初始化享元工廠            FlyweightFactory factory = new FlyweightFactory();            // 判斷是否已經建立了字母A,如果已經建立就直接使用建立的對象A            Flyweight fa = factory.GetFlyweight("A");            if (fa != null)            {                // 把外部狀態作為享元對象的方法調用參數                fa.Operation(--externalstate);            }            // 判斷是否已經建立了字母B            Flyweight fb = factory.GetFlyweight("B");            if (fb != null)            {                fb.Operation(--externalstate);            }            // 判斷是否已經建立了字母C            Flyweight fc = factory.GetFlyweight("C");            if (fc != null)            {                fc.Operation(--externalstate);            }            // 判斷是否已經建立了字母D            Flyweight fd= factory.GetFlyweight("D");            if (fd != null)            {                fd.Operation(--externalstate);            }            else            {                Console.WriteLine("駐留池中不存在字串D");                // 這時候就需要建立一個對象並放入駐留池中                ConcreteFlyweight d = new ConcreteFlyweight("D");                factory.flyweights.Add("D", d);            }            Console.Read();        }    }    /// <summary>    /// 享元工廠,負責建立和管理享元對象    /// </summary>    public class FlyweightFactory    {        // 最好使用泛型Dictionary<string,Flyweighy>        //public Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>();        public Hashtable flyweights = new Hashtable();        public FlyweightFactory()        {            flyweights.Add("A", new ConcreteFlyweight("A"));            flyweights.Add("B", new ConcreteFlyweight("B"));            flyweights.Add("C", new ConcreteFlyweight("C"));        }        public Flyweight GetFlyweight(string key)        {// 更好的實現如下            //Flyweight flyweight = flyweights[key] as Flyweight;            //if (flyweight == null)            //{            //    Console.WriteLine("駐留池中不存在字串" + key);            //    flyweight = new ConcreteFlyweight(key);            //}            //return flyweight;return flyweights[key] as Flyweight;        }    }    /// <summary>    ///  抽象享元類,提供具體享元類具有的方法    /// </summary>    public abstract class Flyweight    {        public abstract void Operation(int extrinsicstate);    }    // 具體的享元對象,這樣我們不把每個字母設計成一個單獨的類了,而是作為把共用的字母作為享元對象的內部狀態    public class ConcreteFlyweight : Flyweight    {        // 內部狀態        private string intrinsicstate ;        // 建構函式        public ConcreteFlyweight(string innerState)        {            this.intrinsicstate = innerState;        }        /// <summary>        /// 享元類的執行個體方法        /// </summary>        /// <param name="extrinsicstate">外部狀態</param>        public override void Operation(int extrinsicstate)        {            Console.WriteLine("具體實作類別: intrinsicstate {0}, extrinsicstate {1}", intrinsicstate, extrinsicstate);        }    }

  在享元模式的實現中,我們沒有像之前一樣,把一個細粒度的類執行個體設計成一個單獨的類,而是把它作為共用對象的內部狀態放在共用類的內部定義,具體的解釋注釋中都有了,大家可以參考注釋去進一步理解享元模式。

2.3 享元模式的類圖

  看完享元模式的實現之後,為了協助大家理清楚享元模式中各類之間的關係,下面給出上面實現代碼中的類圖,如下所示:

在中,涉及的角色如下幾種角色:

抽象享元角色(Flyweight):此角色是所有的具體享元類的基類,為這些類規定出需要實現的公用介面。那些需要外部狀態的操作可以通過調用方法以參數形式傳入。

具體享元角色(ConcreteFlyweight):實現抽象享元角色所規定的介面。如果有內部狀態的話,可以在類內部定義。

享元工廠角色(FlyweightFactory):本角色複雜建立和管理享元角色。本角色必須保證享元對象可以被系統適當地共用,當一個用戶端對象調用一個享元對象的時候,享元工廠角色檢查系統中是否已經有一個符合要求的享元對象,如果已經存在,享元工廠角色就提供已存在的享元對象,如果系統中沒有一個符合的享元對象的話,享元工廠角色就應當建立一個合適的享元對象。

用戶端角色(Client):本角色需要儲存所有享元對象的外部狀態。

註:上面的實現只是單純的享元模式,同時還有複合的享元模式,由於複合享元模式較複雜,這裡就不給出實現了。

三、享元模式的優缺點

分析完享元模式的實現之後,讓我們繼續分析下享元模式的優缺點:

優點:

  1,降低了系統中對象的數量,從而降低了系統中細粒度對象給記憶體帶來的壓力。

缺點:

  1,為了使對象可以共用,需要將一些狀態外部化,這使得程式的邏輯更複雜,使系統複雜化。
  2,享元模式將享元對象的狀態外部化,而讀取外部狀態使得已耗用時間稍微變長。


四、使用情境


在下面所有條件都滿足時,可以考慮使用享元模式:

  1,一個系統中有大量的對象;
  2,這些對象耗費大量的記憶體;
  3,這些對象中的狀態大部分都可以被外部化
  4,這些對象可以按照內部狀態分成很多的組,當把外部對象從對象中剔除時,每一個組都可以僅用一個對象代替
  5,軟體系統不依賴這些對象的身份,


滿足上面的條件的系統可以使用享元模式。但是使用享元模式需要額外維護一個記錄子系統已有的所有享元的表,而這也需要耗費資源,所以,應當在有足夠多的享元執行個體可共用時才值得使用享元模式。

 

註:在.NET類庫中,string類的實現就使用了享元模式,更多內容可以參考字串駐留池的介紹,同時也可以參考這個博文深入理解.NET中string類的設計——http://www.cnblogs.com/artech/archive/2010/11/25/internedstring.html

五、總結
到這裡,享元模式的介紹就結束了,享元模式主要用來解決由於大量的細粒度對象所造成的記憶體開銷的問題,它在實際的開發中並不常用,可以作為底層的提升效能的一種手段。

 

以上內容摘抄自:http://learninghard.blog.51cto.com/6146675/1315781

延伸閱讀:Flyweight Pattern

設計模式專題(learninghard)

設計模式專題(呂震宇)

 

設計模式(十二)享元模式(Flyweight Pattern)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.