基于面向对象思想设计的应用程序有时遇到须要场景大量相同或显示对象实例的场景,这些数量庞大的实例极可能会消耗不少系统资源,最直接的就是内存了。好比要一款围棋游戏,若是每次落子都新建一个对象,将会占用大量内存,而实际上棋子只有黑白两色,不一样的只是落子位置而已。另外,大量的主动型对象还会占用不少CPU和显卡的计算资源,举个极端的例子,某个游戏的沙漠场景,为了使游戏具备丰富的视觉效果,要求每一粒沙子都要随着光线而有不一样的呈现效果不一样,这时候直接new固然也是不现实的。设计模式
享元模式
享元模式提供了一种针对这类场景的解决方案。它经过共享已经存在的对象来大幅度减小须要建立的对象数量、避免大量类似对象的开销,从而提升系统资源的利用率,支持大量细粒度对象的复用。缓存
UML类图:
ide
代码实现this
public abstract class Flyweight { //内部状态 public string Instrinsic { get; set; } //外部状态 protected string Extrinsic { get; set; } public Flyweight(string extrinsic) { this.Extrinsic = extrinsic; } //定义业务操做 public abstract void Operate(int id); } public class ConcreteFlyweight : Flyweight { //接受外部状态 public ConcreteFlyweight(String extrinsic) : base(extrinsic) { } //根据外部状态进行逻辑处理 public override void Operate(int id) { Console.WriteLine("Flyweight:" + id); } } public class UnsharedConcreteFlyweight : Flyweight { public UnsharedConcreteFlyweight(String extrinsic) : base(extrinsic) { } public override void Operate(int id) { Console.WriteLine("不共享的Flyweight:" + id); } } public class FlyweightFactory { //定义一个池容器 private static Dictionary<String, Flyweight> pool = new Dictionary<String, Flyweight>(); //享元工厂 public static Flyweight GetFlyweight(string extrinsic) { Flyweight flyweight = null; if (pool.ContainsKey(extrinsic)) { flyweight = pool[extrinsic]; Console.Write($"已有{extrinsic} "); } else { flyweight = new ConcreteFlyweight(extrinsic); pool.Add(extrinsic, flyweight); Console.Write($"新建{extrinsic} "); } return flyweight; } }
享元模式的核心是用一个池容器来缓存须要共享的对象,C#能够用Dictionary来实现。spa
内部状态与外部状态
因为这些数量较大的细粒度对象有着相近的性质,为了能共享这些对象,须要将这些对象的信息分为两个部分:内部状态和外部状态。
设计
- 内部状态指对象共享出来的信息,存储在享元对象内部而且不会随环境的改变而改变;
- 外部状态指会随环境改变而改变的、不可共享的状态。
好比前面围棋的例子中,棋子的黑白两色就可做为内部状态,落子位置则是外部状态,将内部状态(黑、白两色)做为对象间的本质区别,只须要两个对象就能够了,而后配合外部状态(落子位置)的变化,就能够表示所有的棋子。
围棋例子的误导
使用围棋这个例子会容易让人产生一个困惑:既然实际上只有两个对象(黑、白),那么是如何让同一个对象即出如今位置A,又出如今位置B的呢?难不成像薛定谔的猫那样,能够有多种状态?
实际上这里所谓的共享对象,共享的应该是对象的行为。在围棋游戏中能够理解为,在画布上绘制的棋子图案。每一个棋子对象都有个绘制棋子图案的行为,经过设置不一样的外部状态(落子位置),就棋子的绘图行为就会在画布上不一样的位置绘制图案。
code
享元模式的适用场景
- 系统中有大量对象。
- 这些对象消耗大量内存。
- 这些对象的状态大部分能够外部化。
- 这些对象能够按照内部状态分为不少组,当把外部对象从对象中剔除出来时,每一组对象均可以用一个对象来代替。
- 系统不依赖于这些对象的身份,这些对象是不可分辨的。
享元模式的优缺点
优势对象
- 大大减小对象的建立,下降系统的资源占用,提升效率。
- 因为抽离出了外部状态和内部状态,外部状态相对独立,不会影响到内部状态,因此享元模式使得享元对象可以在不一样的环境被共享。
缺点 - 增长了系统的复杂度,须要分离出外部状态和内部状态,并且外部状态具备固有化的性质,不该该随着内部状态的变化而变化,不然会形成系统的混乱。
- 为了使对象能够共享,享元模式须要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
参考书籍:
王翔著 《设计模式——基于C#的工程化实现及扩展》
blog