设计模式的征途—12.享元(Flyweight)模式
現(xiàn)在在大力推行節(jié)約型社會(huì),“浪費(fèi)可恥,節(jié)儉光榮”。在軟件系統(tǒng)中,有時(shí)候也會(huì)存在資源浪費(fèi)的情況,例如,在計(jì)算機(jī)內(nèi)存中存儲(chǔ)了多個(gè)完全相同或者非常相似的對(duì)象,如果這些對(duì)象的數(shù)量太多將導(dǎo)致系統(tǒng)運(yùn)行代價(jià)過(guò)高。那么,是否存在一種技術(shù)可以用于節(jié)約內(nèi)存使用空間,實(shí)現(xiàn)對(duì)這些相同或者相似對(duì)象的共享訪問(wèn)呢?答案是肯定的,這種技術(shù)就是享元模式。
| 享元模式(Flyweight) | 學(xué)習(xí)難度:★★★★☆ | 使用頻率:★☆☆☆☆ |
一、圍棋棋子的設(shè)計(jì)
M公司開(kāi)發(fā)部欲開(kāi)發(fā)一個(gè)圍棋軟件,其界面效果如下圖所示:
M公司開(kāi)發(fā)人員通過(guò)對(duì)圍棋軟件進(jìn)行分析,發(fā)現(xiàn)在圍棋棋盤(pán)中包含大量的黑子和白子,它們的形狀、大小都一模一樣,只是出現(xiàn)的位置不同而已。如果將每一個(gè)棋子都作為一個(gè)獨(dú)立的對(duì)象存儲(chǔ)在內(nèi)存中,將可能導(dǎo)致該圍棋軟件在運(yùn)行時(shí)所需要的內(nèi)存空間較大。
如何降低運(yùn)行代價(jià)、提高系統(tǒng)性能是M公司開(kāi)發(fā)人員需要解決的一個(gè)問(wèn)題。為此,M公司開(kāi)發(fā)人員決定使用享元模式來(lái)設(shè)計(jì)該軟件。
二、享元模式概述
2.1 享元模式簡(jiǎn)介
享元(Flyweight)模式:運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度對(duì)象的復(fù)用。系統(tǒng)只使用少量的對(duì)象,而這些對(duì)象都很相似,狀態(tài)變化很小,可以實(shí)現(xiàn)對(duì)象的多次復(fù)用。由于享元模式要求能夠共享的對(duì)象必須是細(xì)粒度對(duì)象,因此它又稱(chēng)為輕量級(jí)模式,是一種結(jié)構(gòu)型模式。
2.2 享元模式結(jié)構(gòu)
享元模式的結(jié)構(gòu)較為復(fù)雜,一般結(jié)合工廠模式一起使用,在其結(jié)構(gòu)圖中包含了一個(gè)享元工廠類(lèi),如下圖所示:
由上圖可以看出,它包含了以下4個(gè)角色:
(1)Flyweight(抽象享元類(lèi)):一個(gè)接口或抽象類(lèi),聲明了具體享元類(lèi)的公共方法。
(2)ConcreteFlyweight(具體享元類(lèi)):實(shí)現(xiàn)了抽象享元類(lèi),其實(shí)例稱(chēng)為享元對(duì)象。
(3)UnsharedConcreteFlyweight(非共享具體享元類(lèi)):并不是所有的抽象享元類(lèi)的子類(lèi)都需要被共享,不能被共享的子類(lèi)可設(shè)計(jì)為費(fèi)共享具體享元類(lèi)。
(4)FlyweightFactory(享元工廠類(lèi)):用于創(chuàng)建并管理享元對(duì)象,一般設(shè)計(jì)為一個(gè)存儲(chǔ)“Key-Value”鍵值對(duì)的集合(可以結(jié)合工廠模式設(shè)計(jì))。其作用就在于:提供一個(gè)用于存儲(chǔ)享元對(duì)象的享元池,當(dāng)用戶(hù)需要對(duì)象時(shí),首先從享元池中獲取,如果享元池中不存在,那么則創(chuàng)建一個(gè)新的享元對(duì)象返回給用戶(hù),并在享元池中保存該新增對(duì)象。=> 想想.NET中的各種資源池的設(shè)計(jì)?
三、重構(gòu)后的圍棋棋子方案
3.1 重構(gòu)后的設(shè)計(jì)
M公司開(kāi)發(fā)人員采用了享元模式進(jìn)行設(shè)計(jì),如下圖所示:
其中,IgoChessman充當(dāng)抽象享元類(lèi),BlackIgoChessman和WhiteIgoChessman充當(dāng)具體享元類(lèi),IgoChessmanFactory充當(dāng)享元工廠類(lèi)。
3.2 具體代碼實(shí)現(xiàn)
(1)抽象享元類(lèi):IgoChessman
public abstract class IgoChessman{public abstract string GetColor();public void Display(Coordinates coord){Console.WriteLine("棋子顏色:{0},棋子位置:{1}", GetColor(), coord.X + "," + coord.Y);}}/// <summary>/// 外部狀態(tài):棋子坐標(biāo)/// </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)具體享元類(lèi):BlackIgoChessman、WhiteIgoChessman
// 具體享元類(lèi)Apublic class BlackIgoChessman : IgoChessman{public override string GetColor(){return "黑色";}}// 具體享元類(lèi)Bpublic class WhiteIgoChessman : IgoChessman{public override string GetColor(){return "白色";}}(3)享元工廠類(lèi):IgoChessmanFactory
/// <summary>/// 享元工廠類(lèi)/// </summary>public class IgoChessmanFactory{private static readonly IgoChessmanFactory instance = new IgoChessmanFactory(); // 使用單例模式實(shí)現(xiàn)享元private static Hashtable ht; // 使用Hashtable來(lái)存儲(chǔ)享元對(duì)象,充當(dāng)享元池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)客戶(hù)端調(diào)用
public class Program{public static void Main(string[] args){// 獲取享元工廠IgoChessmanFactory chessFactory = IgoChessmanFactory.GetInstance();// 通過(guò)享元工廠獲取3顆黑子IgoChessman blackChess1 = chessFactory.GetIgoChessman("b");IgoChessman blackChess2 = chessFactory.GetIgoChessman("b");IgoChessman blackChess3 = chessFactory.GetIgoChessman("b");Console.WriteLine("判斷兩顆黑子是否相同:{0}", object.ReferenceEquals(blackChess1, blackChess2));// 通過(guò)享元工廠獲取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();}}運(yùn)行結(jié)果如下圖所示:
從運(yùn)行結(jié)果可以看出,在每次調(diào)用Display()方法時(shí),都設(shè)置了不同的外部狀態(tài)-坐標(biāo)值,因此相同的棋子雖然具有相同的顏色,但是它們的坐標(biāo)值不同,將顯示在棋盤(pán)的不同位置。
四、享元模式總結(jié)
4.1 主要優(yōu)點(diǎn)
可以極大減少內(nèi)存中對(duì)象的數(shù)量,使得相同或相似對(duì)象在內(nèi)存中只有一份 => 節(jié)省系統(tǒng)資源,提高系統(tǒng)性能!棒棒噠!
4.2 主要缺點(diǎn)
為了使對(duì)象可以共享,享元模式需要將享元對(duì)象的部分狀態(tài)外部化,而讀取外部狀態(tài)將使得運(yùn)行時(shí)間變長(zhǎng)!
4.3 應(yīng)用場(chǎng)景
(1)一個(gè)系統(tǒng)有大量相同或相似的對(duì)象,造成了系統(tǒng)內(nèi)存的大量損耗 => 趕緊使用享元模式吧!
(2)對(duì)象的大部分狀態(tài)都可以外部化,可以將這些外部狀態(tài)傳入對(duì)象中。
(3)要維護(hù)享元模式,需要耗費(fèi)一定的系統(tǒng)資源,因?yàn)樵谛枰獣r(shí)會(huì)多次重復(fù)使用才值得使用享元模式了!
參考資料
劉偉,《設(shè)計(jì)模式的藝術(shù)—軟件開(kāi)發(fā)人員內(nèi)功修煉之道》
?
作者:周旭龍
出處:http://edisonchou.cnblogs.com
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文鏈接。
轉(zhuǎn)載于:https://www.cnblogs.com/edisonchou/p/7148258.html
總結(jié)
以上是生活随笔為你收集整理的设计模式的征途—12.享元(Flyweight)模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [导入]给家人补补钙!双莲炖腔骨
- 下一篇: java中对象模型与数据库中的关系模型