C#设计模式之十一享元模式(Flyweight Pattern)【结构型】
一、引言
今天我们要讲【结构型】设计模式的第六个模式,该模式是【享元模式】,英文名称是:Flyweight Pattern。还是老套路,先从名字上来看看。“享元”是不是可以这样理解,共享“单元”,单元是什么呢,举例说明,对于图形而言就是图元,对于英文来说就只26个英文字母,对于汉语来说就是每个汉字,也可以这样理解“元”,构成事物的最小单元,这些单元如果大量、且重复出现,可以缓存重复出现的单元,达到节省内存的目的,换句说法就是享元是为了节省空间,对于计算机而言就是内存。面向对象很好地解决了系统抽象性的问题(系统抽象性指把系统里面的事物写成类,类可以实例化成为对象,用对象和对象之间的关系来设计系统),在大多数情况下,这样做是不会损及系统的性能的。但是,在某些特殊的应用中,由于对象的数量太大,并且这些大量的对象中有很多是重复的,如果每个对象都单独的创建(C#的语法是new)出来,会给系统带来难以承受的内存开销。比如图形应用中的图元等对象、字处理应用中的字符对象等。
二、享元模式的详细介绍
2.1、动机(Motivate)
在软件系统中,采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?
2.2、意图(Intent)
运用共享技术有效地支持大量细粒度的对象。 ——《设计模式》GoF
2.3、结构图(Structure)
i
2.4、模式的组成
(1)、抽象享元角色(Flyweight):此角色是所有的具体享元类的基类,为这些类规定出需要实现的公共接口。那些需要外部状态的操作可以通过调用方法以参数形式传入。
(2)、具体享元角色(ConcreteFlyweight):实现抽象享元角色所规定的接口。如果有内部状态的话,可以在类内部定义。
(3)、享元工厂角色(FlyweightFactory):本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享,当一个客户端对象调用一个享元对象的时候,享元工厂角色检查系统中是否已经有一个符合要求的享元对象,如果已经存在,享元工厂角色就提供已存在的享元对象,如果系统中没有一个符合的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
(4)、客户端角色(Client):本角色需要存储所有享元对象的外部状态。
2.5、享元模式的具体代码实现
说起“享元模式”,我这里有一个很好的场景可以进行说明。我们知道在战斗的游戏场景中,会有很多战士,基本上战士都是差不多的,小区别战士忽略,最大的区别就是拿的武器不同而已。在大型的战争游戏中,会有大量的士兵出来战斗,我们写程序的时候就可以用“享元”来解决大量战士的情况。
namespace 享元模式的实现
{
/// <summary>
/// 享元模式不是很难,但是有些状态需要单独处理,以下就是该模式的C#实现,有些辅助类,大家应该看得出吧,别混了。
/// </summary>
class Client
{
static void Main(string[] args)
{
//比如,我们现在需要10000个一般士兵,只需这样
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();
}
} //这些是辅助类型
public enum SoldierType
{
Normal,
Water
} //该类型就是抽象战士Soldier--该类型相当于抽象享元角色
public abstract class Soldier
{
//通过构造函数初始化士兵的名称
protected Soldier(string name)
{
this.Name = name;
} //士兵的名字
public string Name { get; private set; } //可以传入不同的武器就用不同的活力---该方法相当于抽象Flyweight的Operation方法
public abstract void Fight(); public Weapen WeapenInstance { get; set; }
} //一般类型的战士,武器就是步枪---相当于具体的Flyweight角色
public sealed class NormalSoldier : Soldier
{
//通过构造函数初始化士兵的名称
public NormalSoldier(string name) : base(name) { } //执行享元的方法---就是Flyweight类型的Operation方法
public override void Fight()
{
WeapenInstance.Fire("士兵:"+Name+" 在陆地执行击毙任务");
}
} //这是海军陆战队队员,武器精良----相当于具体的Flyweight角色
public sealed class WaterSoldier : Soldier
{
//通过构造函数初始化士兵的名称
public WaterSoldier(string name) : base(name) { } //执行享元的方法---就是Flyweight类型的Operation方法
public override void Fight()
{
WeapenInstance.Fire("士兵:"+Name+" 在海中执行击毙任务");
}
} //此类型和享元没太大关系,可以算是享元对象的状态吧,需要从外部定义
public abstract class Weapen
{
public abstract void Fire(string jobName);
} //此类型和享元没太大关系,可以算是享元对象的状态吧,需要从外部定义
public sealed class AK47:Weapen
{
public override void Fire(string jobName)
{
Console.WriteLine(jobName);
}
} //该类型相当于是享元的工厂---相当于FlyweightFactory类型
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;
}
}
}
这个模式很简单,就话不多说了。
三、享元模式的实现要点:
面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。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#设计模式之十一享元模式(Flyweight Pattern)【结构型】的更多相关文章
- 设计模式(十)享元模式Flyweight(结构型)
设计模式(十)享元模式Flyweight(结构型) 说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释 ...
- 设计模式系列之享元模式(Flyweight Pattern)——实现对象的复用
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- 设计模式--享元模式Flyweight(结构型)
一.享元模式 在一个系统中如果有多个相同的对象,这些对象有部分状态是可以共享的,我们运用共享技术就能有效地支持大量细粒度的对象. 二.例子 举个围棋的例子,围棋的棋盘共有361格,即可放361个棋子. ...
- 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern)
原文:乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) 作者:weba ...
- 设计模式-11享元模式(Flyweight Pattern)
1.模式动机 在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题.创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈. 享元模式就是把相同或相似对象的公共部分提取出 ...
- 二十四种设计模式:享元模式(Flyweight Pattern)
享元模式(Flyweight Pattern) 介绍运用共享技术有效地支持大量细粒度的对象. 示例有一个Message实体类,某些对象对它的操作有Insert()和Get()方法,现在要运用共享技术支 ...
- Java享元模式(Flyweight Pattern)
享元模式(Flyweight Pattern)主要用于减少创建的对象数量,并减少内存占用并提高性能. 这种类型的设计模式属于结构模式,因为该模式提供了减少对象计数的方法,从而改善应用的对象结构. 享元 ...
- 【转】设计模式(十一)代理模式Proxy(结构型)
设计模式(十一)代理模式Proxy(结构型) 1.概述 因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ...
- 设计模式学习心得<享元模式 Flyweight>
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式. 享元模式尝 ...
随机推荐
- 微信支付接口调用H5(C#)
H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付.主要用于触屏版的手机浏览器请求微信支付的场景.可以方便的从外部浏览器唤 ...
- java.lang.OutOfMemoryError: PermGen space (jvm内存泄漏解决办法)
2.在myeclipse根目录 打开myeclipse.ini 3.在myeclipse中配置内存
- [转] Linux 内核中的 Device Mapper 机制
本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...
- Windows 10 IoT Serials 10 – 如何使用OCR引擎进行文字识别
1. 引言 OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗.亮的模式确定其形状,然后用字符识别方 ...
- 原来你离BAT只有一步之遥
ladies and乡亲们 喜迎全民嗨购双11 i春秋准备搞一波大优惠 优惠力度有多大 跨店凑单满400-50? 指定商品199减100? 史无钜惠 不凑单 不指定 一次直降9000元 原价:2580 ...
- [W3bsafe]分享一个爬SQL注入漏洞的工具
分享一个爬SQL注入的工具 本文转自:i春秋社区由团队核心成员若间开发把工具放到E盘的一个文件夹 他会自动生成一个文本文件 Result.txt 最大页数 自己想弄填多少就填多少关键词 注入点关键词 ...
- Java 线程池(ThreadPoolExecutor)原理解析
在我们的开发中“池”的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:<关于java多线程w ...
- js代码跑马灯效果-----轮播图字效果!
文字元素: <p id="yc-msg">你有本事来打我呀!</p> js执行代码: function ycMsg() { // 获取 标签 var pOb ...
- 关于动画的几种状态表示的含义以及能够使用2d动画表述为什么要使用3d动画表述
transform 四种转换 translate 位置scale 缩放rotate 旋转skew 倾斜 以上四种转换方式是比较特殊的,其实他们都是由matrix 矩阵转换来: animation的五种 ...
- [原创]k8exe2bat任意文件转Bat工具(WebShell无法上传EXE解决方案)
http://qqhack8.blog.163.com/blog/static/114147985201126105626755/ 这是我2011年的东西了,当时用此方法可免杀很多马,至今依然有很大的 ...