十四.结构型设计模式——Flyweight Pattern(轻量级模式)
-
定义
运用共享技术有效地支持大量细粒度的对象,系统只使用少量的对象,而这些对象都相近,状态变化很小,对象使用次数增多。
UML类图如下:
其中类和对象的关系:
1. Flyweight(抽象轻量级类):声明一个接口,通过它可以接受外来的参数(状态),并对新状态做出出来(作用)。
2. ConcreteFlyweight(具体轻量级类):实现Flyweight的接口,并为内部状态增加存储空间,ConcreteFlyweight对象必须是可以共享的,它所有存储的状态必须是内部的,它独立存在于自己的环境中。
3. UnsharedConcreteFlyweight(不共享具体轻量级类):不是所有的Flyweight子类都需要包儿共享,Flyweight的共享不是强制的。在某些Flyweight的结构层次中,UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点。
4. FlyweightFactory(轻量级类工厂):创建并管理flyweight对象;确保享用flyweight。当用户请求一个flyweight时,FlyweightFactory提供一个已创建的实例或者创建一个实例。
5. Client(客户应用程序):维持一个对flyweight的引用;计算或存储一个或多个flyweight的外部状态。
典型应用的顺序图如图:
客户初次从轻量级类工厂取flyweight时,轻量级类工厂创建一个新的具体flyweight对象,并且保存起来,下次客户取用时,就不用重新创建,直接在保存池中返回。客户负责处理flyweight的状态。
-
实例1——文档编辑器
潜在的,每个字符都被大量的采用,因此共享这些字符对象将会节省大量的存储空间。文档编辑器的UML类图如下:
//Flyweight工厂
class CharacterFactotry
{
private Hashtable characters = new Hashtable();
public Character GetCharacter(char key)
{
//在初始化的Hashtable中取出字符
Character character = (Character)characters[key];
//如果取出的字符为null,初始化它
if (character == null)
{
switch (key)
{
case 'A':
character = new CharacterA();
break;
case 'B':
character = new CharacterB();
break;
case 'Z':
character = new CharacterZ();
break;
}
characters.Add(key, character);
}
return character;
}
}
//Flyweight
abstract class Character
{
protected char symbol;
protected int width;
protected int height;
protected int ascent;
protected int descent;
protected int pointSize;
public abstract void Draw(int pointSize);
}
class CharacterA : Character
{
public CharacterA()
{
this.symbol = 'A';
this.height = 100;
this.width = 120;
this.ascent = 70;
this.descent = 0;
}
public override void Draw(int pointSize)
{
this.pointSize = pointSize;
Console.WriteLine(this.symbol);
}
}
class CharacterB : Character
{
public CharacterB()
{
this.symbol = 'B';
this.height = 100;
this.width = 140;
this.ascent = 72;
this.descent = 0;
}
public override void Draw(int pointSize)
{
this.pointSize = pointSize;
Console.WriteLine(this.symbol);
}
}
class CharacterZ : Character
{
public CharacterZ()
{
this.symbol = 'Z';
this.height = 100;
this.width = 100;
this.ascent = 68;
this.descent = 0;
}
public override void Draw(int pointSize)
{
this.pointSize = pointSize;
Console.WriteLine(this.symbol);
}
}
//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
//用字符数组创造document
char[] document = { 'A', 'B', 'Z', 'Z', 'A', 'A' };
CharacterFactotry f = new CharacterFactotry();
//外部状态
int pointSize = 12;
//为每一个字符使用一个flyweight对象
foreach (char c in document)
{
Character character = f.GetCharacter(c);
character.Draw(pointSize);
}
Console.Read();
}
}
-
优势和缺陷
Flyweight模式需要你认真考虑如何能细化对象,以减少处理的对象数量,从而减少存留对象在内存或其他存储设备中的占用量。然而,此模式需要维护大量对象的外部状态,如果外部状态的数据量大,传递、查找、计算这些恶数据会变得非常复杂。当外部和内部的状态很难分清时,不宜采用flyweight模式。
-
应用情景
下面的情景很适合应用轻量级模式:
1. 系统需要存在大量的对象而共享某些本质的、不变的信息。
2. 对象可以同时用于多个环境下。
3. 在每个实例下,flyweight可以作为一个独立的对象。