引言

在软件开发过程,如果我们需要重复使用某个对象的时候,如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非常严重,然而享元模式可以解决这个问题,下面具体看看享元模式是如何去解决这个问题的。

享元模式的详细介绍

在前面说了,享元模式可以解决上面的问题了,在介绍享元模式之前,让我们先要分析下如果去解决上面那个问题,上面的问题就是重复创建了同一个对象,如果让我们去解决这个问题肯定会这样想:“既然都是同一个对象,能不能只创建一个对象,然后下次需要创建这个对象的时候,让它直接用已经创建好了的对象就好了”,也就是说——让一个对象共享。不错,这个也是享元模式的实现精髓所在。

定义

介绍完享元模式的精髓之后,让我们具体看看享元模式的正式定义:

享元模式——运用共享技术有效地支持大量细粒度的对象。享元模式可以避免大量相似类的开销,在软件开发中如果需要生成大量细粒度的类实例来表示数据,如果这些实例除了几个参数外基本上都是相同的,这时候就可以使用享元模式来大幅度减少需要实例化类的数量。如果能把这些参数(指的这些类实例不同的参数)移动类实例外面,在方法调用时将他们传递进来,这样就可以通过共享大幅度地减少单个实例的数目。(这个也是享元模式的实现要领),然而我们把类实例外面的参数称为享元对象的外部状态,把在享元对象内部定义称为内部状态。具体享元对象的内部状态与外部状态的定义为:

内部状态:在享元对象的内部并且不会随着环境的改变而改变的共享部分

外部状态:随环境改变而改变的,不可以共享的状态。

  • Flyweight----抽象享元角色
          简单的说就是一个产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。 
  • ConcreteFlyweight----具体享元角色
          具体的一个产品类,实现抽象角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改       变了内部状态,同时修改了外面状态,这是绝对不允许的。 
  • unsharedConcreteFlyweight----不可共享的享元角色
         不存在外部状态或者安全要求(如线程安全)不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。 
  • FlyweightFactory----享元工厂
         职责非常简单,就是构造一个池容器,同时提供从池中获得对象的方法。
 
具体实现代码如下:
   /// <summary>
    /// 抽象享元角色
    /// </summary>
    public abstract class Flyweight
    {
        //内部状态
        private string intrinsic;
        //外部状态
        protected readonly string Extrinsic;
        //要求享元角色必须接受外部状态
        public Flyweight(string _Extrinsic)
        {
            this.Extrinsic = _Extrinsic;
        }
        //定义业务操作
        public abstract void operate();
        public string Intrinsic
        {
            set
            {
                this.intrinsic = value;
            }
            get
            {
                return this.intrinsic;
            }
        }
    }
    /// <summary>
    /// 具体享元角色
    /// </summary>
    public class ConcreteFlyweight1 : Flyweight
    {
        //接受外部状态
        public ConcreteFlyweight1(string _Extrinsic)
            : base(_Extrinsic)
        {
        }
        //根据外部状态进行逻辑处理
        public override void operate()
        {
            //业务逻辑
        }
    }
    /// <summary>
    /// 具体享元角色
    /// </summary>
    public class ConcreteFlyweight2 : Flyweight
    {
        //接受外部状态
        public ConcreteFlyweight2(string _Extrinsic)
            : base(_Extrinsic)
        {
        }
        //根据外部状态进行逻辑处理
        public override void operate()
        {
            //业务逻辑
        }
    }
    /// <summary>
    /// 享元工厂
    /// </summary>
    public class FlyweightFactory
    {
        //定义一个池容器
        private static IDictionary<string, Flyweight> pool = new Dictionary<string, Flyweight>();
        //享元工厂
        public static Flyweight getFlyweight(string Extrinsic)
        {
            Flyweight flyweight = null;
            if (pool.ContainsKey(Extrinsic))
            {
                flyweight = pool[Extrinsic];
            }
            else
            {
                flyweight = new ConcreteFlyweight1(Extrinsic);
                pool[Extrinsic] = flyweight;
            }
            return flyweight;
        }
    }
View Code

 

享元模式的优缺点

分析完享元模式的实现之后,让我们继续分析下享元模式的优缺点:

优点:

  1. 降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

缺点:

  1. 为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑更复杂,使系统复杂化。
  2. 享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。

使用场景

在下面所有条件都满足时,可以考虑使用享元模式:

  • 一个系统中有大量的对象;
  • 这些对象耗费大量的内存;
  • 这些对象中的状态大部分都可以被外部化
  • 这些对象可以按照内部状态分成很多的组,当把外部对象从对象中剔除时,每一个组都可以仅用一个对象代替
  • 软件系统不依赖这些对象的身份,

满足上面的条件的系统可以使用享元模式。但是使用享元模式需要额外维护一个记录子系统已有的所有享元的表,而这也需要耗费资源,所以,应当在有足够多的享元实例可共享时才值得使用享元模式。