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

来源:http://www.cnblogs.com/xiongzaiqiren/archive/2016/03/28/5328393.html
-Advertisement-
Play Games

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


一、引言

  在軟體開發過程,如果我們需要重覆使用某個對象的時候,如果我們重覆地使用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)

設計模式專題(呂震宇)

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 後盾網教程統一大集合整理(內容較多,請自備紙巾) 傳送門:http://fu83.cn/thread-162-1-1.html ...
  • 什麼叫架構?揭開架構神秘的面紗,無非就是:分層+模塊化。任意複雜的架構,你也會發現架構師也就做了這兩件事。 本文將會全面的介紹我們團隊在模塊化設計方面取得的經驗。之所以加了“全面”二字,是因為本文的內容將會涉及到:資料庫、路由、C#、JavaScript、CSS、HTML等一個完整模塊所需要的內容。 ...
  • 序言 在.Net的世界中,一提及SOA,大家想到的應該是Web Service,WCF,還有人或許也會在.NET MVC中的Web API上做上標記,然後泛泛其談! 的確,微軟的這些技術也確實推動著面向服務的世界發展,當然除了微軟還有很多面向服務的開源技術,甚至在某些方面比微軟做的更加優秀。那麼什麼 ...
  • 一、前言 面向對象技術可以很好地解決一些靈活性或可擴展性問題,但在很多情況下需要在系統中增加類和對象的個數。當對象數量太多時,將導致運行代價過高,帶來性能下降等問題。 享元模式正是為解決這一類問題而誕生的。享元模式通過共用技術實現相同或相似對象的重用。 在享元模式中通常會出現工廠模式,需要創建一個享 ...
  • * 軟體腐化的原因: 問題所在 設計目標 過於僵硬 可擴展性(新性能可以很容易加入系統)過於脆弱 靈活性(修改不會波及其它)復用率低 粘度過高 可插入性(新功能容易加入系統(氣囊加入方向盤)) * 提高系統可復用性的幾點原則:傳統復用:1. 代碼的粘帖復用2. 演算法的復用3. 數據結構的復用 * 可 ...
  • 一、引言 提到模板,大家肯定不免想到生活中的“簡歷模板”、“論文模板”、“Word中模版文件”等,在現實生活中,模板的概念就是——有一個規定的格式,然後每個人都可以根據自己的需求或情況去更新它,例如簡歷模板,下載下來的簡歷模板的格式都是相同的,然而我們下載下來簡歷模板之後我們可以根據自己的情況填充不 ...
  • 一、引言 在軟體開發過程中,有些對象有時候會由於網路或其他的障礙,以至於不能夠或者不能直接訪問到這些對象,如果直接訪問對象給系統帶來不必要的複雜性,這時候可以在客戶端和目標對象之間增加一層中間層,讓代理對象代替目標對象,然後客戶端只需要訪問代理對象,由代理對象去幫我們去請求目標對象並返回結果給客戶端 ...
  • 引:通常我們在編寫代碼的時候全部都是用別人設計好的庫和框架。我們利用他們的API編譯成我們的程式、享受運用別人的代碼所帶來的優點。我們從中挑選所要的組件,把它們運用在合適的地方。但是,庫與框架無法幫助我們將應用組織成容易瞭解、容易維護、具有彈性的架構,所以需要設計模式。(廣告詞) OO基礎 抽象 封 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...