设计模式之享元模式

一、 享元(Flyweight)模式(转贴)

Flyweight在拳击比赛中指最轻量级,即"蝇量级",有些作者翻译为"羽量级"。这里使用"享元模式"更能反映模式的用意。

享元模式以共享的方式高效地支持大量的细粒度对象。享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。内蕴状态是存储在享元对象内部并且不会随环境改变而改变。因此内蕴状态并可以共享。

外蕴状态是随环境改变而改变的、不可以共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态与内蕴状态是相互独立的。

享元模式的应用

享元模式在编辑器系统中大量使用。一个文本编辑器往往会提供很多种字体,而通常的做法就是将每一个字母做成一个享元对象。享元对象的内蕴状态就是这 个字母,而字母在文本中的位置和字模风格等其他信息则是外蕴状态。比如,字母a可能出现在文本的很多地方,虽然这些字母a的位置和字模风格不同,但是所有 这些地方使用的都是同一个字母对象。这样一来,字母对象就可以在整个系统中共享。


二、 单纯享元模式的结构

Pic86.gif

在单纯享元模式中,所有的享元对象都是可以共享的。单纯享元模式所涉及的角色如下:

抽象享元(Flyweight)角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。那些需要外蕴状态(External State)的操作可以通过调用商业方法以参数形式传入。

具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享的。

享元工厂(FlyweightFactory)角色:本角色负责创建和管理享元角色。 本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个复合要求的享元对象。如 果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。

客户端(Client)角色:本角色需要维护一个对所有享元对象的引用。本角色需要自行存储所有享元对象的外蕴状态。


三、 单纯享元模式的示意性源代码

None.gif //  Flyweight pattern -- Structural example  
None.gif
using  System;
None.gif
using  System.Collections;
None.gif
None.gif
//  "FlyweightFactory"
None.gif
class  FlyweightFactory
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// Fields
InBlock.gif
  private Hashtable flyweights = new Hashtable();
InBlock.gif
InBlock.gif  
// Constructors
InBlock.gif
  public FlyweightFactory()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    flyweights.Add(
"X"new ConcreteFlyweight());
InBlock.gif    flyweights.Add(
"Y"new ConcreteFlyweight());
InBlock.gif    flyweights.Add(
"Z"new ConcreteFlyweight());
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
// Methods
InBlock.gif
  public Flyweight GetFlyweight(string key)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
return((Flyweight)flyweights[ key ]);
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
None.gif
//  "Flyweight"
None.gif
abstract   class  Flyweight
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// Methods
InBlock.gif
  abstract public void Operation( int extrinsicstate );
ExpandedBlockEnd.gif}

None.gif
None.gif
//  "ConcreteFlyweight"
None.gif
class  ConcreteFlyweight : Flyweight
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
private string intrinsicstate = "A";
InBlock.gif  
// Methods
InBlock.gif
  override public void Operation( int extrinsicstate )
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    Console.WriteLine(
"ConcreteFlyweight: intrinsicstate {0}, extrinsicstate {1}"
InBlock.gif      intrinsicstate, extrinsicstate );
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
ExpandedBlockStart.gifContractedBlock.gif
/**/ /// <summary>
InBlock.gif
/// Client test
ExpandedBlockEnd.gif
/// </summary>

None.gif public   class  Client
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
public static void Main( string[] args )
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
// Arbitrary extrisic state
InBlock.gif
    int extrinsicstate = 22;
InBlock.gif     
InBlock.gif    FlyweightFactory f 
= new FlyweightFactory();
InBlock.gif
InBlock.gif    
// Work with different flyweight instances
InBlock.gif
    Flyweight fx = f.GetFlyweight("X");
InBlock.gif    fx.Operation( 
--extrinsicstate );
InBlock.gif
InBlock.gif    Flyweight fy 
= f.GetFlyweight("Y");
InBlock.gif    fy.Operation( 
--extrinsicstate );
InBlock.gif
InBlock.gif    Flyweight fz 
= f.GetFlyweight("Z");
InBlock.gif    fz.Operation( 
--extrinsicstate );
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}


四、 复合享元模式的结构

单纯享元模式中,所有的享元对象都可以直接共享。下面考虑一个较为复杂的情况,即将一些单纯享元使用合成模式加以复合,形成复合享元对象。这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。

复合享元模式的类图如下图所示:

Pic87.gif

享元模式所涉及的角色有抽象享元角色、具体享元角色、复合享元角色、享员工厂角色,以及客户端角色等。

抽象享元角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口。那些需要外蕴状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。

具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接 口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候 具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。

复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。

享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。 本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象, 如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。

客户端(Client)角色:本角色还需要自行存储所有享元对象的外蕴状态。

注:由于复合享元模式比较复杂,这里就不再给出示意性代码。通过将享元模式与合成模式组合在一起,可以确保复合享元中所包含的每个单纯享元都具有相同的外蕴状态,而这些单纯享元的内蕴状态往往不同。该部分内容可以参考《Java与模式》第31章内容。


五、 一个咖啡摊的例子

在这个咖啡摊(Coffee Stall)所使用的系统里,有一系列的咖啡"风味(Flavor)"。客人到摊位上购买咖啡,所有的咖啡均放在台子上,客人自己拿到咖啡后就离开摊位。 咖啡有内蕴状态,也就是咖啡的风味;咖啡没有环境因素,也就是说没有外蕴状态。如果系统为每一杯咖啡都创建一个独立的对象的话,那么就需要创建出很多的细 小对象来。这样就不如把咖啡按照种类(即"风味")划分,每一种风味的咖啡只创建一个对象,并实行共享。

使用咖啡摊主的语言来讲,所有的咖啡都可按"风味"划分成如Capucino、Espresso等,每一种风味的咖啡不论卖出多少杯,都是全同、不 可分辨的。所谓共享,就是咖啡风味的共享,制造方法的共享等。因此,享元模式对咖啡摊来说,就意味着不需要为每一份单独调制。摊主可以在需要时,一次性地 调制出足够一天出售的某一种风味的咖啡。

很显然,这里适合使用单纯享元模式。系统的设计如下:

None.gif using  System;
None.gif
using  System.Collections;
None.gif
None.gif
public   abstract   class  Order
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// 将咖啡卖给客人
InBlock.gif
  public abstract void Serve();
InBlock.gif  
// 返回咖啡的名字
InBlock.gif
  public abstract string GetFlavor();
ExpandedBlockEnd.gif}

None.gif
None.gif
public   class  Flavor : Order
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
private string flavor;
InBlock.gif
InBlock.gif  
// 构造函数,内蕴状态以参数方式传入
InBlock.gif
  public Flavor(string flavor)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
this.flavor = flavor;
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
// 返回咖啡的名字
InBlock.gif
  public override string GetFlavor()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
return this.flavor;
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
// 将咖啡卖给客人
InBlock.gif
  public override void Serve()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    Console.WriteLine(
"Serving flavor " + flavor);
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
None.gif
public   class  FlavorFactory
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
private Hashtable flavors = new Hashtable();
InBlock.gif
InBlock.gif  
public Order GetOrder(string key)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
if(! flavors.ContainsKey(key))
InBlock.gif      flavors.Add(key, 
new Flavor(key));
InBlock.gif
InBlock.gif        
return ((Order)flavors[key]);
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
public int GetTotalFlavorsMade()
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    
return flavors.Count;
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif
None.gif
public   class  Client
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
private static FlavorFactory flavorFactory;
InBlock.gif  
private static int ordersMade = 0;
InBlock.gif
InBlock.gif  
public static void Main( string[] args )
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif    flavorFactory 
= new FlavorFactory();
InBlock.gif
InBlock.gif    TakeOrder(
"Black Coffee");
< SPAN>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值