享元模式(Flyweight):
定义:
运用共享技术有效地支持大量细粒度对象的复用。享元模式可以避免大量相似类的开销,在软件开发中如果需要生成大量细粒度的类实例,而这些类实例除了几个参数外基本上相同,那么这时就可以使用享元模式大幅度减少实例化类的数量。如果能把这些参数移动到实例外,在方法调用时将他们传递进去,这样就可以通过共享大幅度减少单个实例的数目。这里我们把移动到类实例外部的参数称为享元对象的外部状态,把在享元对象内部定义称为内部状态。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,他是一种结构型设计模式。享元模式结构较为复杂,一般结合工厂模式一起使用。
1)外部状态:随环境改变而改变的,不可以共享的状态。
2)内部状态:在享元对象内部并且不会随着环境的变化而改变的共享部分。
享元模式的角色:
1)抽象享元类(Flyweight):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
2)具体享元类(ConcreteFlyweight):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
3)非共享具体享元类(UnsharedConcreteFlyweight):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
4)享元工厂类(FlyweightFactory):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
1 public class Program 2 { 3 private static void Main(string[] args) 4 { 5 // 定义外部状态,例如字母的位置等信息 6 int externalstate = 10; 7 FlyweightFactory factory = new FlyweightFactory(); 8 Flyweight fa = factory.GetFlyweight("A"); 9 if (fa != null) 10 { 11 fa.Operation(--externalstate); 12 } 13 14 // 判断是否已经创建了字母B 15 Flyweight fb = factory.GetFlyweight("B"); 16 if (fb != null) 17 { 18 fb.Operation(--externalstate); 19 } 20 21 // 判断是否已经创建了字母C 22 Flyweight fc = factory.GetFlyweight("C"); 23 if (fc != null) 24 { 25 fc.Operation(--externalstate); 26 } 27 28 // 判断是否已经创建了字母D 29 Flyweight fd = factory.GetFlyweight("D"); 30 if (fd != null) 31 { 32 fd.Operation(--externalstate); 33 } 34 else 35 { 36 Console.WriteLine("驻留池里面不存在字符串D"); 37 ConcreteFlyweight d = new ConcreteFlyweight("D"); 38 factory.flyweighrs.Add("D", d); 39 d.Operation(--externalstate); 40 } 41 } 42 } 43 44 /// <summary> 45 /// 抽象享元类,提供具体享元类具有的方法 46 /// </summary> 47 public abstract class Flyweight 48 { 49 public abstract void Operation(int extrinsicState); 50 } 51 52 /// <summary> 53 /// 具体享元类,把共享的字母作为享元对象的内部状态 54 /// </summary> 55 public class ConcreteFlyweight : Flyweight 56 { 57 /// <summary> 58 /// 内蕴状态 59 /// </summary> 60 private string intrinsicState; 61 62 /// <summary> 63 /// 构造函数 64 /// </summary> 65 /// <param name="innerState">内蕴状态</param> 66 public ConcreteFlyweight(string innerState) 67 { 68 this.intrinsicState = innerState; 69 } 70 71 /// <summary> 72 /// 享元类的实例方法 73 /// </summary> 74 /// <param name="extrinsicstate">外蕴状态</param> 75 public override void Operation(int extrinsicState) 76 { 77 Console.WriteLine($"具体实现类:intrinsicstate {intrinsicState}, extrinsicstate {extrinsicState}"); 78 } 79 } 80 81 public class FlyweightFactory 82 { 83 public Hashtable flyweighrs = new Hashtable(); 84 85 public FlyweightFactory() 86 { 87 flyweighrs.Add("A", new ConcreteFlyweight("A")); 88 flyweighrs.Add("B", new ConcreteFlyweight("B")); 89 flyweighrs.Add("C", new ConcreteFlyweight("C")); 90 } 91 92 public Flyweight GetFlyweight(string key) 93 { 94 return flyweighrs[key] as Flyweight; 95 } 96 }
享元模式的优缺点:
优点:降低系统中的对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
缺点:1)为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑更复杂,使系统复杂化。
2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
享元模式与原型模式的区别:
1)享元模式是结构型设计模式,而原型模式是创建型设计模式。
2)原型模式关注的是类的重复创建问题,而享元模式关注的是对象的创建问题。
3)原型模式创建的对象属性完全一样,而享元模式会根据不同的外部状态创建不一样的对象实例。
通过搜索我发现有许多关于享元模式与单例模式的区别,现总结如下:
1)享元设计模式是一个类有很多对象,而单例是一个类仅一个对象。
2)享元模式是为了节约内存空间,提升程序性能(避免大量的new操作),而单例模式则主要是出于共享状态的目的。
参考:https://baike.baidu.com/item/%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F