设计模式学习笔记(二十:享元方式)

设计模式学习笔记(二十:享元模式)

1.1概述

    运用共享技术有效地支持大量细粒度的对象。这就是享元模式的定义。

    一个类中的成员变量表明该类所创建对象所具有的属性,在某些程序设计中可能用一个类创建若干个对象,但是发现这些对象的一个共同特点是它们有一部分属性的取值必须是完全相同的。

  例如,一个Car类,其类图如下图一所示:

 设计模式学习笔记(二十:享元方式)

图一:Car

  当用Car类创建若干个的同型号的轿车时,比如创建若干个“奥迪A6”轿车,要求这些轿车的heightwidthlength值都必须是相同的(轿车的属性很多,属于细粒度对象,而且轿车的很多属性值是相同的,这里只示意了heightwidthlength三个属性),而colorpower可以是不同的,就像很多“奥迪A6”轿车,他们的长度、高度和宽度都是相同的,但颜色和动力可能不同。

  从创建对象的角度来看,我们面对的问题是:Car的每个对象的变量都各自占有不同的内存空间。这样一来,Car创建的对象越多就越浪费内存,而且程序也无法保证Car类创建的多个对象所对应的heightwidthlength值是相同的或者禁止Car类对象随意修改自己的heightwidthlengh值。

  现在重新设计Car类,由于要求Car类所创建的若干个对象的heightwidthlength值都必须是相同的,因此没有必要为每个Car对象的heightwidthlength分配不同的内存空间,现在将Car类中的heightwidthlength封装到另一个CarData类中,CarData类的类图如下图二所示:

 设计模式学习笔记(二十:享元方式)

图二:CarData

  现在,假设系统能保证向Car类的若干个对象提供相同的CarData实例,即可以让Car类的若干个对象共享CarData类的一个实例,那么就可以对Car类进行修改,修改后的Car类包含CarData实例,修改后的Car类类图如下图三所示:

 设计模式学习笔记(二十:享元方式)

图三:修改后的Car

  这样一来,Car类创建的若干个对象的colorpower都分配不同的内存空间,但是这些对象共享一个由系统提供的carData对象,节省了内存开销。

 

1.2模式的结构

享元模式包括以下三种角色:

(1)享元接口(Flyweight):是一个接口,该接口定义了享元对外公开其内部数据的方法,以及享元接收外部数据的方法。

(2)具体享元(Concrete Flyweight):实现享元接口的类,该类的实例称为享元对象,或简称享元。具体享元类的成员变量为享元对象的内部状态,享元对象的内部状态必须与所处的周围环境无关,即要保证使用享元对象的应用程序无法更改享元的内部状态,只有这样才能使享元对象在系统中被共享。因为享元对象是用来共享的,所以不能允许用户各自地使用具体享元类来创建对象,这样就无法达到共享的目的,因为不同用户用具体享元类创建的对象显然是不同的,所以,具体享元类的构造方法必须是private的,其目的是不允许用户程序直接使用具体享元类来创建享元对象,创建和管理享元对象有享元工厂负责。

(3)享元工厂(Flyweight Factory):享元工厂是一个类,该类的实例负责创建和管理享元对象,用户或其他对象必须请求享元工厂为它得到一个享元对象。享元工厂可以通过一个散列表(也称作共享池)来管理享元对象,当用户程序或其他若干个对象向享元工厂请求一个享元对象时,如果享元工厂的散列表中已有这样的享元对象,享元工厂就提供这个享元对象给请求者,否则就创建一个享元对象添加到散列表中,同时将该享元对象提供给请求者。显然,当若干个用于或对象请求享元工厂提供一个享元对象时,第一个用户获得该享元对象的时间可能慢一些,但是后继的用户会较快地获得这个享元对象。可以使用单例模式来设计这个享元工厂,即让系统中只有一个享元工厂的实例,另外,为了让享元工厂能生成享元对象,需要将具体享元类作为享元工厂的内部类。

享元模式结构的类图如下图四所示:

 设计模式学习笔记(二十:享元方式)

图四:享元模式的类图

 

1.3享元模式的优点

(1)使用享元可以节省内存的开销,特别适合处理大量细粒度对象,这些对象的许多属性值是相同的,而且一旦创建则不容许修改。

(2)享元模式中的享元可以使用方法的参数接受外部状态中的数据,但外部状态数据不会干扰到享元中的内部数据,这就使享元可以在不同的环境中被共享。

 

 

1.4适合使用享元模式的情景

1)一个应用程序使用大量的对象,这些对象之间部分属性本质上是相同的,这时应使用享元来封装相同的部分。

2)对象的多数状态都可改变为外部状态,就可以考虑将这样对象作为系统中的享元来使用。