1. 程式人生 > >設計模式的征途—12.享元(Flyweight)模式

設計模式的征途—12.享元(Flyweight)模式

現在在大力推行節約型社會,“浪費可恥,節儉光榮”。在軟體系統中,有時候也會存在資源浪費的情況,例如,在計算機記憶體中儲存了多個完全相同或者非常相似的物件,如果這些物件的數量太多將導致系統執行代價過高。那麼,是否存在一種技術可以用於節約記憶體使用空間,實現對這些相同或者相似物件的共享訪問呢?答案是肯定的,這種技術就是享元模式。

享元模式(Flyweight) 學習難度:★★★★☆ 使用頻率:★☆☆☆☆

一、圍棋棋子的設計

M公司開發部欲開發一個圍棋軟體,其介面效果如下圖所示:

  M公司開發人員通過對圍棋軟體進行分析,發現在圍棋棋盤中包含大量的黑子和白子,它們的形狀、大小都一模一樣,只是出現的位置不同而已。如果將每一個棋子都作為一個獨立的物件儲存在記憶體中,將可能導致該圍棋軟體在執行時所需要的記憶體空間較大。

  如何降低執行代價、提高系統性能是M公司開發人員需要解決的一個問題。為此,M公司開發人員決定使用享元模式來設計該軟體。

二、享元模式概述

2.1 享元模式簡介

享元(Flyweight)模式:運用共享技術有效地支援大量細粒度物件的複用。系統只使用少量的物件,而這些物件都很相似,狀態變化很小,可以實現物件的多次複用。由於享元模式要求能夠共享的物件必須是細粒度物件,因此它又稱為輕量級模式,是一種結構型模式。

2.2 享元模式結構

  享元模式的結構較為複雜,一般結合工廠模式一起使用,在其結構圖中包含了一個享元工廠類,如下圖所示:

  由上圖可以看出,它包含了以下4個角色:

  (1)Flyweight(抽象享元類):一個介面或抽象類,聲明瞭具體享元類的公共方法。

  (2)ConcreteFlyweight(具體享元類):實現了抽象享元類,其例項稱為享元物件。

  (3)UnsharedConcreteFlyweight(非共享具體享元類):並不是所有的抽象享元類的子類都需要被共享,不能被共享的子類可設計為費共享具體享元類。

  (4)FlyweightFactory(享元工廠類):用於建立並管理享元物件,一般設計為一個儲存“Key-Value”鍵值對的集合(可以結合工廠模式設計)。其作用就在於:提供一個用於儲存享元物件的享元池,當用戶需要物件時,首先從享元池中獲取,如果享元池中不存在,那麼則建立一個新的享元物件返回給使用者,並在享元池中儲存該新增物件。=> 想想.NET中的各種資源池的設計?

三、重構後的圍棋棋子方案

3.1 重構後的設計

  M公司開發人員採用了享元模式進行設計,如下圖所示:

  其中,IgoChessman充當抽象享元類,BlackIgoChessman和WhiteIgoChessman充當具體享元類,IgoChessmanFactory充當享元工廠類。

3.2 具體程式碼實現

  (1)抽象享元類:IgoChessman

    public abstract class IgoChessman
    {
        public abstract string GetColor();

        public void Display(Coordinates coord)
        {
            Console.WriteLine("棋子顏色:{0},棋子位置:{1}", GetColor(), coord.X + "," + coord.Y);
        }
    }

    /// <summary>
    /// 外部狀態:棋子座標
    /// </summary>
    public class Coordinates
    {
        public int X { get; set; }
        public int Y { get; set; }

        public Coordinates()
        {

        }

        public Coordinates(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
    }

  (2)具體享元類:BlackIgoChessman、WhiteIgoChessman

    // 具體享元類A
    public class BlackIgoChessman : IgoChessman
    {
        public override string GetColor()
        {
            return "黑色";
        }
    }

    // 具體享元類B
    public class WhiteIgoChessman : IgoChessman
    {
        public override string GetColor()
        {
            return "白色";
        }
    }

  (3)享元工廠類:IgoChessmanFactory

    /// <summary>
    /// 享元工廠類
    /// </summary>
    public class IgoChessmanFactory
    {
        private static readonly IgoChessmanFactory instance = new IgoChessmanFactory(); // 使用單例模式實現享元
        private static Hashtable ht;    // 使用Hashtable來儲存享元物件,充當享元池

        private IgoChessmanFactory()
        {
            ht = new Hashtable();
            IgoChessman blackChess = new BlackIgoChessman();
            ht.Add("b", blackChess);
            IgoChessman whiteChess = new WhiteIgoChessman();
            ht.Add("w", whiteChess);
        }

        public static IgoChessmanFactory GetInstance()
        {
            return instance;
        }

        public IgoChessman GetIgoChessman(string color)
        {
            IgoChessman chess = ht[color] as IgoChessman;
            return chess;
        }
    }

  (4)客戶端呼叫

    public class Program
    {
        public static void Main(string[] args)
        {
            // 獲取享元工廠
            IgoChessmanFactory chessFactory = IgoChessmanFactory.GetInstance();
            // 通過享元工廠獲取3顆黑子
            IgoChessman blackChess1 = chessFactory.GetIgoChessman("b");
            IgoChessman blackChess2 = chessFactory.GetIgoChessman("b");
            IgoChessman blackChess3 = chessFactory.GetIgoChessman("b");

            Console.WriteLine("判斷兩顆黑子是否相同:{0}", object.ReferenceEquals(blackChess1, blackChess2));
            // 通過享元工廠獲取2顆白子
            IgoChessman whiteChess1 = chessFactory.GetIgoChessman("w");
            IgoChessman whiteChess2 = chessFactory.GetIgoChessman("w");

            Console.WriteLine("判斷兩顆白子是否相同:{0}", object.ReferenceEquals(whiteChess1, whiteChess2));
            // 顯示棋子
            blackChess1.Display(new Coordinates(1,2));
            blackChess2.Display(new Coordinates(3, 4));
            blackChess3.Display(new Coordinates(1, 3));
            whiteChess1.Display(new Coordinates(2, 5));
            whiteChess2.Display(new Coordinates(2, 4));

            Console.ReadKey();
        }
    }

  執行結果如下圖所示:

  

  從執行結果可以看出,在每次呼叫Display()方法時,都設定了不同的外部狀態-座標值,因此相同的棋子雖然具有相同的顏色,但是它們的座標值不同,將顯示在棋盤的不同位置。

四、享元模式總結

4.1 主要優點

  可以極大減少記憶體中物件的數量,使得相同或相似物件在記憶體中只有一份 => 節省系統資源,提高系統性能!棒棒噠!

4.2 主要缺點

  為了使物件可以共享,享元模式需要將享元物件的部分狀態外部化,而讀取外部狀態將使得執行時間變長!

4.3 應用場景

  (1)一個系統有大量相同或相似的物件,造成了系統記憶體的大量損耗 => 趕緊使用享元模式吧!

  (2)物件的大部分狀態都可以外部化,可以將這些外部狀態傳入物件中。

  (3)要維護享元模式,需要耗費一定的系統資源,因為在需要時會多次重複使用才值得使用享元模式了!

參考資料

  DesignPattern

  劉偉,《設計模式的藝術—軟體開發人員內功修煉之道》

作者:周旭龍

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。

相關推薦

設計模式征途12.Flyweight模式

現在在大力推行節約型社會,“浪費可恥,節儉光榮”。在軟體系統中,有時候也會存在資源浪費的情況,例如,在計算機記憶體中儲存了多個完全相同或者非常相似的物件,如果這些物件的數量太多將導致系統執行代價過高。那麼,是否存在一種技術可以用於節約記憶體使用空間,實現對這些相同或者相似物件的共享訪問呢?答案是肯定的,這種技

設計模式FlyWeight模式

例子 清理 什麽 public == lean http 變量 -- 設計模式:享元(FlyWeight)模式 一、前言 享元(FlyWeight)模式顧名思義,既是輕量級的,原因就是享元,共享元素,這裏的元素指的是對象。如何共享對象,那就是在檢測對象產生的時候,如

設計模式9—— 結構型 ——Flyweight

介紹 定義:提供了減少物件數量從而改善應用所需的物件結構的方式 說明:運用共享技術有效地支援大量細粒度的物件 型別:結構型 適用場景: 常常應用於系統底層的開發,以便解決系統的效能問題(Java中String的實現,資

Javascript 設計模式Flyweight模式jQuery.single方法程式碼修正

今天閱讀了《Javascript 設計模式》的第九章亨元(Flyweight)模式,在9.13.7(110頁),經測試發現了實現jQuery.single方法的程式碼有誤,錯誤程式碼如下: jQuery.single = (func

【JAVA設計模式12.模式

享元模式將物件資訊分為兩類,一個是不受物件具體資訊影響,即可共享的物件資訊,另一個是不同物件間獨立的資訊。 由於存在可共享的資訊,則可設計一個工廠類,工廠類內部儲存了大量物件,所有物件都有一個共同的資訊,例如:產品類別。 先建立一個享元介面: interface Fly

設計模式征途—23.解釋器Interpreter模式

args 參考資料 轉載 返回 下一個 tle title 缺點 images 雖然目前計算機編程語言有好幾百種,但有時人們還是希望用一些簡單的語言來實現特定的操作,只需要向計算機輸入一個句子或文件,就能按照預定的文法規則來對句子或文件進行解釋。例如,我們想要只輸入一個加法

設計模式征途—22.中介者Mediator模式

我們都用過QQ,它有兩種聊天方式:一是私聊,二是群聊。使用QQ群,一個使用者就可以向多個使用者傳送相同的資訊和檔案,從而無需一一發送,節省大量時間。通過引入群的機制,極大地減少系統中使用者之間的兩兩通訊,使用者與使用者之間的聯絡可以通過群的機制來實現。 在有些軟體中,某些類/物件之間的相互呼叫關係錯綜複

設計模式征途—6.建造者Builder模式

建造者模式又稱為生成器模式,它是一種較為複雜、使用頻率也相對較低的建立型模式。建造者模式為客戶端返回的不是一個簡單的產品,而是一個由多個部件組成的複雜產品。因為,沒有人買車會只買一個方向盤或者輪胎,大家買的都是一輛包含輪胎、方向盤和發動機等多個部件組成的完整汽車。如何將這些部件組裝成一輛完整的汽車並返回給使用

設計模式征途—15.觀察者Observer模式

在日常生活中,交通訊號燈指揮者日益擁擠的城市交通。紅燈亮,汽車停止;綠燈亮,汽車繼續前行;在這個過程中,交通訊號燈是汽車的觀察目標,而汽車則是觀察者。隨著交通訊號燈的變化,汽車的行為也會隨之變化,一盞交通訊號燈可以指揮多輛汽車。在軟體系統中,有些物件之間也存在類似交通訊號燈和汽車之間的關係,一個物件的狀態或行

設計模式征途—1.單例Singleton模式

  單例模式屬於建立型模式的一種,建立型模式是一類最常用的設計模式,在軟體開發中應用非常廣泛。建立型模式將物件的建立和使用分離,在使用物件時無需關心物件的建立細節,從而降低系統的耦合度,讓設計方案更易於修改和擴充套件。每一個建立型模式都在檢視回答3個問題:3W -> 建立什麼(What)、由誰建立(Wh

設計模式征途—8.橋接Bridge模式

在現實生活中,我們常常會用到兩種或多種型別的筆,比如毛筆和蠟筆。假設我們需要大、中、小三種類型的畫筆來繪製12中不同的顏色,如果我們使用蠟筆,需要準備3*12=36支。但如果使用毛筆的話,只需要提供3種型號的毛筆,外加12個顏料盒即可,涉及的物件個數僅為3+12=15,遠遠小於36卻能實現與36支蠟筆同樣的功

設計模式】module模式&&Revealing module 揭示模式

但是 出版 參數傳遞 9.png 自然 指向 們的 private 初級 寫在前面 《head first設計模式》裏有一篇文章,是說使用模式的心智,   1、初學者"心智" :"我要為HELLO WORLD找個模式"   2、中級人員模式: "或許這裏我需要一個單件

【java設計模式】之 單例Singleton模式

1. 單例模式的定義         單例模式(Singleton Pattern)是一個比較簡單的模式,其原始定義如下:Ensure a class has only one instance, and provide a global point of access

【java設計模式】之 建造者Builder模式

        我們還是舉上一節的例子:生產汽車。上一節我們通過模板方法模式控制汽車跑起來的動作,那麼需求是無止境的,現在如果老闆又增加了額外的需求:汽車啟動、停止、鳴笛引擎聲都由客戶自己控制,他想要什麼順序就什麼順序,那該如何做呢? 1. 汽車無休止的改造       

程式設計模式(十二) C++ 代理Proxy模式

2.7 Proxy 代理模式為其他物件提供一種代理以控制對這個物件的訪問。 在需要用比較通用和複雜的物件指標代替簡單的的指標的時候,使用代理模式。有四種常用的情況:        1、遠端代理,也就是為一個物件在不同的地址空間提供區域性代表。這樣可以隱藏一個物件存在於不

C#設計模式(12)——模式Flyweight Pattern

{0} dict 實例方法 通過 總結 享元模式 如何 相同 string類 一、引言 在軟件開發過程,如果我們需要重復使用某個對象的時候,如果我們重復地使用new創建這個對象的話,這樣我們在內存就需要多次地去申請內存空間了,這樣可能會出現內存使用越來越多的情況,這樣的問題

設計模式---對象性能模式模式Flyweight

ret 大量 根據 利用 問題 字母 只讀 時代 帶來 一:概念 通過與其他類似對象共享數據來減少內存占用 如果一個應用程序使用了太多的對象, 就會造成很大的存儲開銷。 特別是對於大量輕量級 (細粒度)的對象,比如在文檔編輯器的設計過程中,我們如果為每個字母

設計模式模式Flyweight

設計模式之享元模式(Flyweight) 本篇為https://github.com/iluwatar/java-design-patterns/tree/master/flyweight閱讀筆記 場景 煉金術士的商店裡擺滿了魔法藥水。許多藥水是相同的,因此不需要為每

BoolanC++設計模式 <九> ——單例模式Singleton模式FlyWeight

“物件效能”模式 面向物件很好的解決了“抽象”的問題,但是必不可免地要付出一定的代價。對於通常情況來講,面向物件的成本大都可以忽略不計。但是某些情況,面向物件所帶來的成本必須謹慎處理。 典型模式Sington Flyweight 單例模式Singleton 保證一個類僅有一個例項,並

設計模式模式Flyweight

 運用共享技術有效地支援大量細粒度的物件。又名“蠅量模式”。  在Java語言中,String型別就是使用了享元模式。String物件是final型別,物件一旦建立就不可改變。在JAVA中字串常量都是存在常量池中的,Java會確保一個字串常量在常量池中只有一個