本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7792973.html,记录一下学习过程以备后续查用。

一、引言

今天我们要讲结构型设计模式的第六个模式--享元模式,先从名字上来看,“享元”可以这样理解--共享“单元”。单元是什么呢?举例说明:对于图形而言就

是图元;对于英文来说就只26个英文字母;对于汉语来说就是每个汉字。也可以这样理解“元”--构成事物的最小单元,这些单元如果大量且重复出现,我们可

以缓存重复出现的单元,达到节省内存的目的。换个说法享元是为了节省空间,对于计算机而言就是节省内存。

面向对象很好地解决了系统抽象性的问题(系统抽象性指把系统里面的事物写成类,类可以实例化成为对象,用对象和对象之间的关系来设计系统),在

大多数情况下,这样做是不会损及系统的性能的。但是,在某些特殊的应用中,由于对象的数量太大,并且这些大量的对象中有很多是重复的,如果每个对

象都单独的创建(C#的语法是new)出来,会给系统带来难以承受的内存开销,比如图形应用中的图元等对象、字处理应用中的字符对象等。

    二、享元模式介绍

享元模式:英文名称--Flyweight Pattern;分类--结构型。

2.1、动机(Motivate)

在软件系统中,采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价--主要指内存需求方面的代价。如何在避

免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?

2.2、意图(Intent)

运用共享技术有效地支持大量细粒度的对象。——《设计模式》GoF

2.3、结构图(Structure)

2.4、模式的组成

1)抽象享元角色(Flyweight):此角色是所有的具体享元类的基类,为这些类规定出需要实现的公共接口。那些需要外部状态的操作可以通过调用方法

以参数形式传入。

2)具体享元角色(ConcreteFlyweight):实现抽象享元角色所规定的接口。如果有内部状态的话,可以在类内部定义。

3)享元工厂角色(FlyweightFactory):本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享,当一个客户端对象调用一个

享元对象的时候,享元工厂角色检查系统中是否已经有一个符合要求的享元对象。如果已经存在,享元工厂角色就提供已存在的享元对象,如果系统中没有

一个符合的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。

4)客户端角色(Client):本角色需要存储所有享元对象的外部状态。

2.5、享元模式的具体代码实现

说起“享元模式”,我这里有一个很好的场景可以进行说明。我们知道在战斗的游戏场景中,会有很多战士,基本上战士都是差不多的,最大的区别就是拿的

武器不同而已。在大型的战争游戏中,会有大量的士兵出来战斗,我们写程序的时候就可以用“享元”来解决大量战士的情况。

    class Program
{
/// <summary>
/// 这些是辅助类型
/// </summary>
public enum SoldierType
{
Normal,
Water
} /// <summary>
/// 该类型就是抽象战士Soldier--该类型相当于抽象享元角色
/// </summary>
public abstract class Soldier
{
//通过构造函数初始化士兵的名称
protected Soldier(string name)
{
Name = name;
} //士兵的名字
public string Name { get; private set; } //传入不同的武器就用不同的活力--该方法相当于抽象Flyweight的Operation方法
public abstract void Fight(); public Weapen WeapenInstance { get; set; }
} /// <summary>
/// 一般类型的战士,武器是步枪--相当于具体的Flyweight角色
/// </summary>
public sealed class NormalSoldier : Soldier
{
//通过构造函数初始化士兵的名称
public NormalSoldier(string name) : base(name) { } //执行享元的方法--就是Flyweight类型的Operation方法
public override void Fight()
{
WeapenInstance.Fire("士兵:" + Name + "在陆地执行击毙任务。");
}
} /// <summary>
/// 这是海军陆战队队员,武器精良--相当于具体的Flyweight角色
/// </summary>
public sealed class WaterSoldier : Soldier
{
//通过构造函数初始化士兵的名称
public WaterSoldier(string name) : base(name) { } //执行享元的方法--就是Flyweight类型的Operation方法
public override void Fight()
{
WeapenInstance.Fire("士兵:" + Name + "在海中执行击毙任务。");
}
} /// <summary>
/// 此类型和享元没太大关系,可以算是享元对象的状态吧,需要从外部定义。
/// </summary>
public abstract class Weapen
{
public abstract void Fire(string jobName);
} /// <summary>
/// 此类型和享元没太大关系,可以算是享元对象的状态吧,需要从外部定义。
/// </summary>
public sealed class AK47 : Weapen
{
public override void Fire(string jobName)
{
Console.WriteLine(jobName);
}
} /// <summary>
/// 该类型相当于是享元的工厂--相当于FlyweightFactory类型
/// </summary>
public sealed class SoldierFactory
{
private static IList<Soldier> soldiers; static SoldierFactory()
{
soldiers = new List<Soldier>();
} Soldier mySoldier = null;
//因为我这里有两种士兵,所以在这里可以增加另外一个参数--士兵类型,原模式里面没有。
public Soldier GetSoldier(string name, Weapen weapen, SoldierType soldierType)
{
foreach (Soldier soldier in soldiers)
{
if (string.Compare(soldier.Name, name, true) == )
{
mySoldier = soldier;
return mySoldier;
}
}
//我们这里就任务名称是唯一的
if (soldierType == SoldierType.Normal)
{
mySoldier = new NormalSoldier(name);
}
else
{
mySoldier = new WaterSoldier(name);
}
mySoldier.WeapenInstance = weapen; soldiers.Add(mySoldier);
return mySoldier;
}
} static void Main(string[] args)
{
#region 享元模式
//比如,我们现在需要100个一般士兵。
SoldierFactory factory = new SoldierFactory();
AK47 ak47 = new AK47();
for (int i = ; i < ; i++)
{
Soldier soldier = null;
if (i <= )
{
soldier = factory.GetSoldier("士兵" + (i + ), ak47, SoldierType.Normal);
}
else
{
soldier = factory.GetSoldier("士兵" + (i + ), ak47, SoldierType.Water);
}
soldier.Fight();
}
//我们有这么多的士兵,但是使用的内存不是很多,因为我们缓存了。
Console.Read();
#endregion
}
}

运行结果如下:

这个模式很简单,就话不多说了。

三、享元模式的实现要点

面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代

价问题,一般不触及面向对象的抽象性问题。

Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。对象的

数量太大从而导致对象内存开销加大--什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。

3.1、享元模式的优点

1)享元模式的优点在于它能够极大的减少系统中对象的个数。

2)享元模式由于使用了外部状态,而外部状态相对独立,不会影响到内部状态,所以享元模式使得享元对象能够在不同的环境被共享。

3.2、享元模式的缺点

1)由于享元模式需要区分外部状态和内部状态,使得应用程序在某种程度上来说更加复杂化了。

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

3.3、在下面所有条件都满足时,可以考虑使用享元模式:

1)一个系统中有大量的对象。

2)这些对象耗费大量的内存。

3)这些对象中的状态大部分都可以被外部化。

4)这些对象可以按照内部状态分成很多的组,当把外部对象从对象中剔除时,每一个组都可以仅用一个对象代替软件系统不依赖这些对象的身份。

满足上面的条件的系统可以使用享元模式,但是使用享元模式需要额外维护一个记录子系统已有的所有享元的表,而这也需要耗费资源,所以,应当在有足

够多的享元实例可共享时才值得使用享元模式。

四、.NET 中享元模式的实现

.NET在C#中有一个Code Behind机制,它表面有一个aspx文件,背后又有一个cs文件,它的编译过程实际上会把aspx文件解析成C#文件,然后编译成dll,

在这个过程中,我们在aspx中写的任何html代码都会转化为literal control,literal control是一个一般的文本控件,它就表示html标记。当这些标记有相同的时候,

构建控件树时就会用到Flyweight模式。它的应用并不是那么频繁,只有在效率空间确实不高的时候我们才用它。

五、总结

刚开始接触这个模式的时候,感觉这个模式不是特别难,在我们编码的过程中也有涉及,但是在学习的过程中也走了不少弯路,任何设计模式都有他特定的

使用场景,小心误用。这个模式在业务系统中相对而言使用的并不多,在类似游戏场景中、字符处理等系统用的比较多。还是老话,通过迭代来使用模式,别

为了模式而模式。

C#设计模式学习笔记:(11)享元模式的更多相关文章

  1. 设计模式-11享元模式(Flyweight Pattern)

    1.模式动机 在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题.创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈. 享元模式就是把相同或相似对象的公共部分提取出 ...

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

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

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

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

  4. [设计模式] 11 享元模式 Flyweight

    转 http://blog.csdn.net/wuzhekai1985/article/details/6670298 问题 在面向对象系统的设计何实现中,创建对象是最为常见的操作.这里面就有一个问题 ...

  5. 《JavaScript设计模式与开发实践》读书笔记之享元模式

    1. 享元模式 享元模式是一种用于性能优化的模式,享元模式的核心是运用共享技术来有效支持大量细粒度的对象 1.1 传统的文件上传方法 以文件上传为例,文件上传功能可以选择依照队列,一个一个的排队上传, ...

  6. C#设计模式之十二享元模式(Flyweight)【结构型】

    一.引言   今天我们要讲[结构型]设计模式的第六个模式,该模式是[享元模式],英文名称是:Flyweight Pattern.还是老套路,先从名字上来看看."享元"是不是可以这样 ...

  7. Java设计模式学习笔记(三) 工厂方法模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...

  8. C#设计模式学习笔记:(23)解释器模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8242238.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第十一个模式-- ...

  9. C#设计模式学习笔记:(12)代理模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7814004.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲结构型设计模式的第七个模式,也是 ...

随机推荐

  1. 多个github账号时,本地配置ssh-key

    由于需要,申请了多个github账号,但是都是在同一台电脑上操作,原来只有一个账号进行ssh操作时,推送没有遇到什么问题,现在有多个账号了,推送的时候就有点懵逼了,下面是根据网上的资料来进行多个账号, ...

  2. 如何修改win7文件夹的显示方式为详细信息

    1.首先对着空白处,鼠标右键单击,然后点击“排列方式” 选一个 还有 你还可以点击“查看” 选择图标大小.详细信息.平铺.列表 等2.点击我的电脑左上角的 组织 按钮 随后选择“文件夹和搜索选项” 再 ...

  3. 分层有限状态机的C++实现

    为了方便我的游戏开发,写了这么一个通用的分层有限状态机.希望在其稳定以后,可以作为一个组件加入到我的游戏引擎当中. 目前使用了std::function来调用回调函数,在未来可能会用委托机制代替. 第 ...

  4. Object类、常用API_2

    主要内容 Object类 Date类 DateFormat类 Calendar类 System类 StringBuilder类 包装类 学习目标 -[ ] 能够说出Object类的特点 -[ ] 能够 ...

  5. 创建dynamics CRM client-side (七) - 用JS 来控制Auto-Save

    在我们的system setting里面, 我们可以设置打开/关闭 auto save的功能. 我们可以用js来控制auto-save this.formOnSave = function (exec ...

  6. zbar+opencv检测图片中的二维码或条形码

    zbar本身自带检测二维码条形码功能,这里使用opencv只是做一些简单的读取图片,灰度图片以及显示条形码和二维码时用到一些绘制 // barcode-qrcodescanner.cpp: 定义控制台 ...

  7. 时间序列数据库(TSDB)初识与选择

    时间序列数据库(TSDB)初识与选择 本文作者由 MageByte 团队的 「借来方向」编写,关注公众号 给你更多硬核技术 背景 这两年互联网行业掀着一股新风,总是听着各种高大上的新名词.大数据.人工 ...

  8. springcloud 依赖版本问题

    SpringCloud 版本: 版本名称 版本 Finchley snapshot版 Edgware snapshot版 Dalston SR1 当前最新稳定版本 Camden SR7 稳定版本 Br ...

  9. C++使用类调用CUDA核函数

    正如CUDA C所称,CUDA对C语言进行了很好的扩展,直接使用C语言可以非常简单方便的调用CUDA核函数.但是当想使用C++的类成员函数直接调用核函数是不可行的,第一,核函数不能作为类的成员函数,第 ...

  10. 使用CStatic显示图片(bmp、ico、png)

    一.显示bmp及ico //h文件 CStatic m_static; //cpp文件 CBitmap bitmap; bitmap.LoadBitmapW(IDB_BITMAP); //加载位图 B ...