现在在大力推行节约型社会,“浪费可耻,节俭光荣”。在软件系统中,有时候也会存在资源浪费的情况,例如,在计算机内存中存储了多个完全相同或者非常相似的对象,如果这些对象的数量太多将导致系统运行代价过高。那么,是否存在一种技术可以用于节约内存使用空间,实现对这些相同或者相似对象的共享访问呢?答案是肯定的,这种技术就是享元模式。

享元模式(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(,));
blackChess2.Display(new Coordinates(, ));
blackChess3.Display(new Coordinates(, ));
whiteChess1.Display(new Coordinates(, ));
whiteChess2.Display(new Coordinates(, )); Console.ReadKey();
}
}

  运行结果如下图所示:

  

  从运行结果可以看出,在每次调用Display()方法时,都设置了不同的外部状态-坐标值,因此相同的棋子虽然具有相同的颜色,但是它们的坐标值不同,将显示在棋盘的不同位置。

四、享元模式总结

4.1 主要优点

  可以极大减少内存中对象的数量,使得相同或相似对象在内存中只有一份 => 节省系统资源,提高系统性能!棒棒哒!

4.2 主要缺点

  为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长!

4.3 应用场景

  (1)一个系统有大量相同或相似的对象,造成了系统内存的大量损耗 => 赶紧使用享元模式吧!

  (2)对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。

  (3)要维护享元模式,需要耗费一定的系统资源,因为在需要时会多次重复使用才值得使用享元模式了!

参考资料

  

  刘伟,《设计模式的艺术—软件开发人员内功修炼之道》

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

设计模式的征途—12.享元(Flyweight)模式的更多相关文章

  1. 设计模式C++描述----12.享元(Flyweight)模式

    一. 概述 在面向对象系统的设计何实现中,创建对象是最为常见的操作. 这里面就有一个问题:如果一个应用程序使用了太多的对象,就会造成很大的存储开销.特别是对于大量轻量级(细粒度)的对象,比如在文档编辑 ...

  2. Java 实现享元(Flyweight)模式

    /** * 字母 * @author stone * */ public class Letter { private String name; public Letter(String name) ...

  3. 十二、享元(Flyweight)模式--结构模式(Structural Pattern)

    Flyweight在拳击比赛中指最轻量级,即"蝇量级",有些作者翻译为"羽量级".这里使用"享元 模式"更能反映模式的用意. 享元模式以共享 ...

  4. python 设计模式之享元(Flyweight)模式

    #写在前面 这个设计模式理解起来很容易.百度百科上说的有点绕口. #享元模式的定义 运用共享技术来有効地支持大量细粒度对象的复用. 它通过共享已经存在的对橡大幅度减少需要创建的对象数量.避免大量相似类 ...

  5. 享元(FlyWeight)模式

    享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式.享元模式尝试 ...

  6. 十一、结构模式之享元(Flyweight)模式

    什么是享元模式 享元模式是对象的结构模式,是运用共享技术来有效的支持大量细粒度的对象.享元对象能做到共享的关键是区分内蕴状态和外蕴状态.一个内蕴状态是存储在享元对象内部,并且是不会随环境改变而有所不同 ...

  7. 设计模式(十)享元模式Flyweight(结构型)

    设计模式(十)享元模式Flyweight(结构型) 说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释 ...

  8. Java设计模式(十一) 享元模式

    原创文章,同步发自作者个人博客 http://www.jasongj.com/design_pattern/flyweight/.转载请注明出处 享元模式介绍 享元模式适用场景 面向对象技术可以很好的 ...

  9. C#设计模式(12)——享元模式(Flyweight Pattern)

    一.引言 在软件开发过程,如果我们需要重复使用某个对象的时候,如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非 ...

随机推荐

  1. 【转】Python-__builtin__与__builtins__的区别与关系(超详细,经典)

    在学习Python时,很多人会问到__builtin__.__builtins__和builtins之间有什么关系.百度或Google一下,有很 多答案,但是这些答案要么不准确,要么只说了一点点,并不 ...

  2. YYModel学习总结YYClassInfo(1)

    OC的run-time 机制,简直像是网络上的猫! 我在开发中很少用到,但是作为iOS开发 人家肯定会问这个东西,所以深入的学习了下. 对于 run-time的入手,YYModel的学习,简直让人美滋 ...

  3. Admob - Google广告接入

    前言 现在免费小游戏及应用的主要收入渠道就是通过接入广告.而Google的Admob适用于全球范围内的广告接入,文档方面及后台管理也是较为完善,接入还是比较便捷的. 不过Google目前还在墙外,虽然 ...

  4. win10 uwp 进度条 WaveProgressControl

    昨天看到了有个大神做出好看的进度条样式,于是我就去抄袭他的代码,但是发现看不懂,于是本文主要翻译就是大神说这个控件如何做. 本文翻译 https://stackoverflow.com/a/46057 ...

  5. Babel运行原理

    前言     之前翻博客园的时候,看到有人朋友分享阿里巴巴的面试题,其中有一道题就是关于ES6转ES5 原理的,当时我看到感觉到自己离去阿里巴巴的路还很远啊,像我们大部分做开发的时候,都只知其然不知 ...

  6. faster-rcnn中ROI_POOIING层的解读

    在没有出现sppnet之前,RCNN使用corp和warp来对图片进行大小调整,这种操作会造成图片信息失真和信息丢失.sppnet这个模型推出来之后(关于这个网络的描述,可以看看之前写的一篇理解:ht ...

  7. (转)UML实践详细经典教程----用例图、顺序图、状态图、类图、包图、协作图

    原文链接:http://dn.codegear.com/article/31863 面向对象的问题的处理的关键是建模问题.建模可以把在复杂世界的许多重要的细节给抽象出.许多建模工具封装了UML(也就是 ...

  8. 242. Valid Anagram(leetcode)

    Given two strings s and t, write a function to determine if t is an anagram of s. For example, s = & ...

  9. 25.Linux-Nor Flash驱动(详解)

    1.nor硬件介绍: 从原理图中我们能看到NOR FLASH有地址线,有数据线,它和我们的SDRAM接口相似,能直接读取数据,但是不能像SDRAM直接写入数据,需要有命令才行 1.1其中我们2440的 ...

  10. Arduino.最小系统面包板搭建

    最早试过用万用板做过最小系统,主要用来烧录芯片 后来为了方便,用面包板也搭了一个最小系统, 但不采用杜邦线,因为飞来飞去的线太乱了 因此就有了这个简洁的版本,先上个成品图 用个烧录器就可以很方便的烧写 ...