溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C#設(shè)計(jì)模式(12)——享元模式(Flyweight Pattern)

發(fā)布時(shí)間:2020-06-11 07:59:19 來(lái)源:網(wǎng)絡(luò) 閱讀:1221 作者:LearningHard 欄目:編程語(yǔ)言

一、引言

在軟件開(kāi)發(fā)過(guò)程,如果我們需要重復(fù)使用某個(gè)對(duì)象的時(shí)候,如果我們重復(fù)地使用new創(chuàng)建這個(gè)對(duì)象的話,這樣我們?cè)趦?nèi)存就需要多次地去申請(qǐng)內(nèi)存空間了,這樣可能會(huì)出現(xiàn)內(nèi)存使用越來(lái)越多的情況,這樣的問(wèn)題是非常嚴(yán)重,然而享元模式可以解決這個(gè)問(wèn)題,下面具體看看享元模式是如何去解決這個(gè)問(wèn)題的。

二、享元模式的詳細(xì)介紹

在前面說(shuō)了,享元模式可以解決上面的問(wèn)題了,在介紹享元模式之前,讓我們先要分析下如果去解決上面那個(gè)問(wèn)題,上面的問(wèn)題就是重復(fù)創(chuàng)建了同一個(gè)對(duì)象,如果讓我們?nèi)ソ鉀Q這個(gè)問(wèn)題肯定會(huì)這樣想:“既然都是同一個(gè)對(duì)象,能不能只創(chuàng)建一個(gè)對(duì)象,然后下次需要?jiǎng)?chuàng)建這個(gè)對(duì)象的時(shí)候,讓它直接用已經(jīng)創(chuàng)建好了的對(duì)象就好了”,也就是說(shuō)——讓一個(gè)對(duì)象共享。不錯(cuò),這個(gè)也是享元模式的實(shí)現(xiàn)精髓所在。

2.1 定義

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

享元模式——運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度的對(duì)象。享元模式可以避免大量相似類的開(kāi)銷,在軟件開(kāi)發(fā)中如果需要生成大量細(xì)粒度的類實(shí)例來(lái)表示數(shù)據(jù),如果這些實(shí)例除了幾個(gè)參數(shù)外基本上都是相同的,這時(shí)候就可以使用享元模式來(lái)大幅度減少需要實(shí)例化類的數(shù)量。如果能把這些參數(shù)(指的這些類實(shí)例不同的參數(shù))移動(dòng)類實(shí)例外面,在方法調(diào)用時(shí)將他們傳遞進(jìn)來(lái),這樣就可以通過(guò)共享大幅度地減少單個(gè)實(shí)例的數(shù)目。(這個(gè)也是享元模式的實(shí)現(xiàn)要領(lǐng)),然而我們把類實(shí)例外面的參數(shù)稱為享元對(duì)象的外部狀態(tài),把在享元對(duì)象內(nèi)部定義稱為內(nèi)部狀態(tài)。具體享元對(duì)象的內(nèi)部狀態(tài)與外部狀態(tài)的定義為:

內(nèi)部狀態(tài):在享元對(duì)象的內(nèi)部并且不會(huì)隨著環(huán)境的改變而改變的共享部分

外部狀態(tài):隨環(huán)境改變而改變的,不可以共享的狀態(tài)。

2.2 享元模式實(shí)現(xiàn)

分析完享元模式的實(shí)現(xiàn)思路之后,相信大家實(shí)現(xiàn)享元模式肯定沒(méi)什么問(wèn)題了,下面以一個(gè)世紀(jì)的應(yīng)用來(lái)實(shí)現(xiàn)下享元模式。這個(gè)例子是:一個(gè)文本編輯器中會(huì)出現(xiàn)很多字面,使用享元模式去實(shí)現(xiàn)這個(gè)文本編輯器的話,會(huì)把每個(gè)字面做成一個(gè)享元對(duì)象。享元對(duì)象的內(nèi)部狀態(tài)就是這個(gè)字面,而字母在文本中的位置和字體風(fēng)格等其他信息就是它的外部狀態(tài)。下面就以這個(gè)例子來(lái)實(shí)現(xiàn)下享元模式,具體實(shí)現(xiàn)代碼如下:

/// <summary>
    /// 客戶端調(diào)用
    /// </summary>
    class Client
    {
        static void Main(string[] args)
        {
            // 定義外部狀態(tài),例如字母的位置等信息
            int externalstate = 10;
            // 初始化享元工廠
            FlyweightFactory factory = new FlyweightFactory();
            // 判斷是否已經(jīng)創(chuàng)建了字母A,如果已經(jīng)創(chuàng)建就直接使用創(chuàng)建的對(duì)象A
            Flyweight fa = factory.GetFlyweight("A");
            if (fa != null)
            {
                // 把外部狀態(tài)作為享元對(duì)象的方法調(diào)用參數(shù)
                fa.Operation(--externalstate);
            }
            // 判斷是否已經(jīng)創(chuàng)建了字母B
            Flyweight fb = factory.GetFlyweight("B");
            if (fb != null)
            {
                fb.Operation(--externalstate);
            }
            // 判斷是否已經(jīng)創(chuàng)建了字母C
            Flyweight fc = factory.GetFlyweight("C");
            if (fc != null)
            {
                fc.Operation(--externalstate);
            }
            // 判斷是否已經(jīng)創(chuàng)建了字母D
            Flyweight fd= factory.GetFlyweight("D");
            if (fd != null)
            {
                fd.Operation(--externalstate);
            }
            else
            {
                Console.WriteLine("駐留池中不存在字符串D");
                // 這時(shí)候就需要?jiǎng)?chuàng)建一個(gè)對(duì)象并放入駐留池中
                ConcreteFlyweight d = new ConcreteFlyweight("D");
                factory.flyweights.Add("D", d);
            }
            Console.Read();
        }
    }
    /// <summary>
    /// 享元工廠,負(fù)責(zé)創(chuàng)建和管理享元對(duì)象
    /// </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)
        {
// 更好的實(shí)現(xiàn)如下
            //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);
    }
    // 具體的享元對(duì)象,這樣我們不把每個(gè)字母設(shè)計(jì)成一個(gè)單獨(dú)的類了,而是作為把共享的字母作為享元對(duì)象的內(nèi)部狀態(tài)
    public class ConcreteFlyweight : Flyweight
    {
        // 內(nèi)部狀態(tài)
        private string intrinsicstate ;
        // 構(gòu)造函數(shù)
        public ConcreteFlyweight(string innerState)
        {
            this.intrinsicstate = innerState;
        }
        /// <summary>
        /// 享元類的實(shí)例方法
        /// </summary>
        /// <param name="extrinsicstate">外部狀態(tài)</param>
        public override void Operation(int extrinsicstate)
        {
            Console.WriteLine("具體實(shí)現(xiàn)類: intrinsicstate {0}, extrinsicstate {1}", intrinsicstate, extrinsicstate);
        }
    }

在享元模式的實(shí)現(xiàn)中,我們沒(méi)有像之前一樣,把一個(gè)細(xì)粒度的類實(shí)例設(shè)計(jì)成一個(gè)單獨(dú)的類,而是把它作為共享對(duì)象的內(nèi)部狀態(tài)放在共享類的內(nèi)部定義,具體的解釋注釋中都有了,大家可以參考注釋去進(jìn)一步理解享元模式。

2.3 享元模式的類圖

看完享元模式的實(shí)現(xiàn)之后,為了幫助大家理清楚享元模式中各類之間的關(guān)系,下面給出上面實(shí)現(xiàn)代碼中的類圖,如下所示:

C#設(shè)計(jì)模式(12)——享元模式(Flyweight Pattern)(摘自http://www.cnblogs.com/zhenyulu/articles/55793.html)

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

抽象享元角色(Flyweight):此角色是所有的具體享元類的基類,為這些類規(guī)定出需要實(shí)現(xiàn)的公共接口。那些需要外部狀態(tài)的操作可以通過(guò)調(diào)用方法以參數(shù)形式傳入。

具體享元角色(ConcreteFlyweight):實(shí)現(xiàn)抽象享元角色所規(guī)定的接口。如果有內(nèi)部狀態(tài)的話,可以在類內(nèi)部定義。

享元工廠角色(FlyweightFactory):本角色復(fù)雜創(chuàng)建和管理享元角色。本角色必須保證享元對(duì)象可以被系統(tǒng)適當(dāng)?shù)毓蚕?,?dāng)一個(gè)客戶端對(duì)象調(diào)用一個(gè)享元對(duì)象的時(shí)候,享元工廠角色檢查系統(tǒng)中是否已經(jīng)有一個(gè)符合要求的享元對(duì)象,如果已經(jīng)存在,享元工廠角色就提供已存在的享元對(duì)象,如果系統(tǒng)中沒(méi)有一個(gè)符合的享元對(duì)象的話,享元工廠角色就應(yīng)當(dāng)創(chuàng)建一個(gè)合適的享元對(duì)象。

客戶端角色(Client):本角色需要存儲(chǔ)所有享元對(duì)象的外部狀態(tài)。

注:上面的實(shí)現(xiàn)只是單純的享元模式,同時(shí)還有復(fù)合的享元模式,由于復(fù)合享元模式較復(fù)雜,這里就不給出實(shí)現(xiàn)了。

三、享元模式的優(yōu)缺點(diǎn)

分析完享元模式的實(shí)現(xiàn)之后,讓我們繼續(xù)分析下享元模式的優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

  1. 降低了系統(tǒng)中對(duì)象的數(shù)量,從而降低了系統(tǒng)中細(xì)粒度對(duì)象給內(nèi)存帶來(lái)的壓力。

缺點(diǎn):

  1. 為了使對(duì)象可以共享,需要將一些狀態(tài)外部化,這使得程序的邏輯更復(fù)雜,使系統(tǒng)復(fù)雜化。

  2. 享元模式將享元對(duì)象的狀態(tài)外部化,而讀取外部狀態(tài)使得運(yùn)行時(shí)間稍微變長(zhǎng)。

四、使用場(chǎng)景

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

  • 一個(gè)系統(tǒng)中有大量的對(duì)象;

  • 這些對(duì)象耗費(fèi)大量的內(nèi)存;

  • 這些對(duì)象中的狀態(tài)大部分都可以被外部化

  • 這些對(duì)象可以按照內(nèi)部狀態(tài)分成很多的組,當(dāng)把外部對(duì)象從對(duì)象中剔除時(shí),每一個(gè)組都可以僅用一個(gè)對(duì)象代替

  • 軟件系統(tǒng)不依賴這些對(duì)象的身份,

滿足上面的條件的系統(tǒng)可以使用享元模式。但是使用享元模式需要額外維護(hù)一個(gè)記錄子系統(tǒng)已有的所有享元的表,而這也需要耗費(fèi)資源,所以,應(yīng)當(dāng)在有足夠多的享元實(shí)例可共享時(shí)才值得使用享元模式。

注:在.NET類庫(kù)中,string類的實(shí)現(xiàn)就使用了享元模式,更多內(nèi)容可以參考字符串駐留池的介紹,同時(shí)也可以參考這個(gè)博文深入理解.NET中string類的設(shè)計(jì)——http://www.cnblogs.com/artech/archive/2010/11/25/internedstring.html

五、總結(jié)

到這里,享元模式的介紹就結(jié)束了,享元模式主要用來(lái)解決由于大量的細(xì)粒度對(duì)象所造成的內(nèi)存開(kāi)銷的問(wèn)題,它在實(shí)際的開(kāi)發(fā)中并不常用,可以作為底層的提升性能的一種手段


附件:http://down.51cto.com/data/2363665
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI