11. Flyweight 享元模式 2008-8-21
面向对象的代价:
面向对象很好的解决了系统抽象性的问题,同时在大多数情况下,也不会损及系统的性能。但是,在某些特殊的应用中下,由于对象的数量太大,采用面向对象会给系统带来难以承受的内存开销。比如图形应用中的图元等对象、文字处理应用中的字符对象等。。。
动机(Motivation)
采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。
如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?
意图(Intent)
运用共享技术有效地支持大量细粒度的对象。 ——《设计模式》GoF
基本代码:
//
FlyweightFactory,是一个享元工厂,用来创建并管理Flyweight对象。它主要是用来确保合理地共享Flyweight,
//
当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。
class
FlyweightFactory
{
private
Hashtable flyweights
=
new
Hashtable();
public
FlyweightFactory()
{
flyweights.Add(
"
X
"
,
new
ConcreteFlyweight());
flyweights.Add(
"
Y
"
,
new
ConcreteFlyweight());
flyweights.Add(
"
Z
"
,
new
ConcreteFlyweight());
}
public
Flyweight GetFlyweight(
string
key)
{
return
((Flyweight)flyweights[key]);
}
}
//
Flyweight类,它是所有具体享元的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态
abstract
class
Flyweight
{
public
abstract
void
Operation(
int
extrinsicstate);
}
//
ConcreteFlyweight是继承Flyweight超类或实现Flyweight接口,并为内部状态增加存储空间。
class
ConcreteFlyweight : Flyweight
{
public
override
void
Operation(
int
extrinsicstate)
{
Console.WriteLine(
"
具体Flyweight:
"
+
extrinsicstate);
}
}
//
UnsharedConcreteFlyweight是指那些不需要共享的Flyweighrt子类,因为Flyweight接口共享成为可能,但它不强制共享
class
UnsharedConcreteFlyweight : Flyweight
{
public
override
void
Operation(
int
extrinsicstate)
{
Console.WriteLine(
"
不共享的具体Flyweight:
"
+
extrinsicstate);
}
}
客户端代码:
class
Program
{
static
void
Main(
string
[] args)
{
int
extrinsicstate
=
22
;
FlyweightFactory f
=
new
FlyweightFactory();
Flyweight fx
=
f.GetFlyweight(
"
X
"
);
fx.Operation(
--
extrinsicstate);
Flyweight fy
=
f.GetFlyweight(
"
Y
"
);
fy.Operation(
--
extrinsicstate);
Flyweight fz
=
f.GetFlyweight(
"
Z
"
);
fz.Operation(
--
extrinsicstate);
UnsharedConcreteFlyweight uf
=
new
UnsharedConcreteFlyweight();
uf.Operation(
--
extrinsicstate);
Console.Read();
}
}
Flyweight 的几个要点
面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
对象的数量太大从而导致对象内存开销加大——什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。
适用性
当以下所有的条件都满足时,可以考虑使用享元模式:
(摘自:http://terrylee.cnblogs.com/archive/2006/03/29/361767.html)
1、一个系统有大量的对象。
2、这些对象耗费大量的内存。
3、这些对象的状态中的大部分都可以外部化。
4、这些对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替。
5、软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。
满足以上的这些条件的系统可以使用享元对象。最后,使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式。