原文: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、享元模式的具体代码实现
说起“享元模式”,我这里有一个很好的场景可以进行说明。我们知道在战斗的游戏场景中,会有很多兵士,根基上兵士都是差不久不多的,小区别兵士忽略,最大的区别就是拿的刀兵差别而已。在大型的战争游戏中,会有大量的士兵出来战斗,我们写措施的时候就可以用“享元”来解决大量兵士的情况。
1 namespace 享元模式的实现 2 { 3 /// <summary> 4 /// 享元模式不是很难,但是有些状态需要单独措置惩罚惩罚,以下就是该模式的C#实现,有些帮助类,大家应该看得出吧,别混了。 5 /// </summary> 6 class Client 7 { 8 static void Main(string[] args) 9 { 10 //好比,我们此刻需要10000个一般士兵,只需这样 11 SoldierFactory factory = new SoldierFactory(); 12 AK47 ak47 = new AK47(); 13 for (int i = 0; i < 100; i++) 14 { 15 Soldier soldier = null; 16 if (i <= 20) 17 { 18 soldier = factory.GetSoldier("士兵" + (i + 1), ak47, SoldierType.Normal); 19 } 20 else 21 { 22 soldier = factory.GetSoldier("士兵" + (i + 1), ak47, SoldierType.Water); 23 } 24 soldier.Fight(); 25 } 26 //我们有这么多的士兵,但是使用的内存不是很多,因为我们缓存了。 27 Console.Read(); 28 } 29 } 30 31 //这些是帮助类型 32 public enum SoldierType 33 { 34 Normal, 35 Water 36 } 37 38 //该类型就是抽象兵士Soldier--该类型相当于抽象享元角色 39 public abstract class Soldier 40 { 41 //通过结构函数初始化士兵的名称 42 protected Soldier(string name) 43 { 44 this.Name = name; 45 } 46 47 //士兵的名字 48 public string Name { get; private set; } 49 50 //可以传入差此外刀兵就用差此外活力---该要领相当于抽象Flyweight的Operation要领 51 public abstract void Fight(); 52 53 public Weapen WeapenInstance { get; set; } 54 } 55 56 //一般类型的兵士,刀兵就是步枪---相当于具体的Flyweight角色 57 public sealed class NormalSoldier : Soldier 58 { 59 //通过结构函数初始化士兵的名称 60 public NormalSoldier(string name) : base(name) { } 61 62 //执行享元的要领---就是Flyweight类型的Operation要领 63 public override void Fight() 64 { 65 WeapenInstance.Fire("士兵:"+Name+" 在陆地执行击毙任务"); 66 } 67 } 68 69 //这是海军陆战队队员,刀兵精良----相当于具体的Flyweight角色 70 public sealed class WaterSoldier : Soldier 71 { 72 //通过结构函数初始化士兵的名称 73 public WaterSoldier(string name) : base(name) { } 74 75 //执行享元的要领---就是Flyweight类型的Operation要领 76 public override void Fight() 77 { 78 WeapenInstance.Fire("士兵:"+Name+" 在海中执行击毙任务"); 79 } 80 } 81 82 //此类型和享元没太大关系,可以算是享元东西的状态吧,需要从外部界说 83 public abstract class Weapen 84 { 85 public abstract void Fire(string jobName); 86 } 87 88 //此类型和享元没太大关系,可以算是享元东西的状态吧,需要从外部界说 89 public sealed class AK47:Weapen 90 { 91 public override void Fire(string jobName) 92 { 93 Console.WriteLine(jobName); 94 } 95 } 96 97 //该类型相当于是享元的工厂---相当于FlyweightFactory类型 98 public sealed class SoldierFactory 99 { 100 private static IList<Soldier> soldiers; 101 102 static SoldierFactory() 103 { 104 soldiers = new List<Soldier>(); 105 } 106 107 Soldier mySoldier = null; 108 //因为我这里有两种士兵,所以在这里可以增加此外一个参数,士兵类型,原模式里面没有, 109 public Soldier GetSoldier(string name, Weapen weapen, SoldierType soldierType) 110 { 111 foreach (Soldier soldier in soldiers) 112 { 113 if (string.Compare(soldier.Name, name, true) == 0) 114 { 115 mySoldier = soldier; 116 return mySoldier; 117 } 118 } 119 //我们这里就任务名称是独一的 120 if (soldierType == SoldierType.Normal) 121 { 122 mySoldier = new NormalSoldier(name); 123 } 124 else 125 { 126 mySoldier = new WaterSoldier(name); 127 } 128 mySoldier.WeapenInstance = weapen; 129 130 soldiers.Add(mySoldier); 131 return mySoldier; 132 } 133 } 134 }