概述:
在项目开发中我们有时候需要控制一些细密度的集合类,通常的做法是系统实例化每一个类进行指定的业务操作,这时系统需要消耗很多的内存,如果类过多的话将会把内存给撑爆掉。有种做法就是不用面向对象,呵呵对!的确可以但这个和你整个系统采用面向对象程序设计又有些格格不入。那么我们如何避免大量细粒度的对象,同时又不影响客户程序使用面向对象的操作呢?我们先来看个简单的超市进销存的例子(这里我精简了一些,列出了部分对象属性。)
需求:将商品做出仓入仓管理
需求很简单,打个比方,最近超市蛋黄派(汗!又是蛋黄派。没办法本人天天早餐就是一个蛋黄派)卖的比较火超市仓库出货进货比较频繁。正常的设计是有一个蛋黄派我就实例化一个,然后存入仓库。这里就出现了很大的问题,如果有1000袋要入仓1000袋要出仓那我们要实例化2000个这样的类,这样对内存消耗和处理性能来说都是有很大负担的。这时我们需要一种方式来解决这个问题,享元模式就是专门处理这样的情况的。享元模式用于处理大量细粒度的对象。那么我们就用它来处理上面的问题
看下图:
解释:
1. 定义一个commodity 商品,对于仓库来讲他们统计商品只需要知道每种商品的条形码和库存量,至于其他的属性没有必要一一了解。
2. 定义一个蛋黄派类实现commodity抽象类中的in 、out方法
3. 定义一个商品供应商CakeAccommodate他组要负责向超市供应商品,当供应商没有经营一个商品时他会立刻建立这个商品的采购渠道保证商品的正常供给
这里也许大家会问怎么说了半天仓库,仓库这个类都没有,这里我做了简化,如果你愿意可以再建一个仓库类和超市类出来,我这里将commodity 设计成了一个存放商品的空间,可能名字取的不好吧!如果你想完善他可以自行设计一个,这里我只是图了个方便省事,首先在在这里声明这不是一个很好的对象设计.只是为了说明享元模式。Commodity这里你可以理解为存放某类商品的存储空间
代码:
private static void SimplenFlyWeigth()
{
CakeAccommodate acc = new CakeAccommodate();
Commodity gm = acc.GetCakes("光明");
gm.In(20);
Commodity wg = acc.GetCakes("卫岗");
wg.In(25);
Commodity wm = acc.GetCakes("无名");
wm.In(10);
gm.Out(30);
gm.In(10);
gm.Out(30);
wg.Out(5);
wm.Out(1);
//Console.ReadLine();
}
效果:
但真正的仓储没那么简单,往往每批货物都是规整放到指定位置的,那么这样就需要我们依据不同的位置存放商品,这里我们引入复合享元模式的机制。通过传入参数动态控制细粒度对象的业务逻辑
看下图:
1. PointCommodity:指定的某种类存储位置
2. ShelfPoint:货架
3. PointCake:存放在指定货架上的蛋黄派
4. CakeAccommodate:蛋黄派供应商
运行效果:
OOD设计合理性:
1. 是否符合开不原则:
如果我们需要再加一种商品进来的话我们只需要新建一个类继承Commodity抽象类就可以了,不用修改其他类
符合
2. 是否符合里氏代换:
符合
3. 是否符合抽象原则
符合
4. 是否符合迪米特法则
符合
享元模式总结
意图:
运用共享技术有效地支持大量细粒度的对象
结构图:
享元模式所涉及的角色有抽象享元角色、具体享元角色、复合享元角色、享员工厂角色,以及客户端角色等。
抽象享元角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。那些需要外蕴状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。
享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
客户端(Client)角色:本角色还需要自行存储所有享元对象的外蕴状态
使用背景:
1. 一个系统有大量的对象。
2. 这些对象耗费大量的内存。
3. 这些对象的状态中的大部分都可以外部化。
4. 这些对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替。
5. 软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。
模式优缺点:
优点:
1. 大大节约了存储空间
备注:
Flyweight在拳击比赛中指最轻量级,即"蝇量级",有些作者翻译为"羽量级"。这里使用"享元模式"更能反映模式的用意。
享元模式以共享的方式高效地支持大量的细粒度对象。享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。内蕴状态是存储在享元对象内部并且不会随环境改变而改变。因此内蕴状态并可以共享。
外蕴状态是随环境改变而改变的、不可以共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态与内蕴状态是相互独立的。