設計模式之Flyweight(享元)

Flyweight 定義 :
避免大量擁有相同內容的小類別的開銷 ( 如耗費記憶體 ), 使大家共用一個類別 ( 元類別 )

爲什麽使用 ?
面向物件語言的原則就是一切都是物件 , 但是如果真正使用起來 , 有時物件數可能顯得很龐大 , 比如 , 文字處理軟體 , 如果以每個文字都作爲一個物件 , 幾千個字 , 物件數就是幾千 , 無疑耗費記憶體 , 那麽我們還是要 " 求同存異 ", 找出這些物件群的共同點 , 設計一個元類別 , 封裝可以被共用的類別 , 另外 , 還有一些特性是取決於應用 (context), 是不可共用的 , 這也 Flyweight 中兩個重要概念內部狀態 intrinsic 和外部狀態 extrinsic 之分。

說白點 , 就是先捏一個的原始模型 , 然後隨著不同場合和環境 , 再産生各具特徵的具體模型 , 很顯然 , 在這裏需要産生不同的新物件 , 所以 Flyweight 模式中常出現 Factory 模式。 Flyweight 的內部狀態是用來共用的 ,Flyweight factory 負責維護一個 Flyweight pool( 模式池 ) 來存放內部狀態的物件。

Flyweight 模式是一個提高程式效率和性能的模式 , 會大大加快程式的運行速度。應用場合很多 : 比如你要從一個資料庫中讀取一系列字串 , 這些字串中有許多是重復的 , 那麽我們可以將這些字串儲存在 Flyweight(pool) 中。

如何使用 ?

我們先從 Flyweight 抽象介面開始 :

public interface Flyweight
{
  
public void operation( ExtrinsicState state );
}

// 用於本模式的抽象資料類型 ( 自行設計 )
public interface ExtrinsicState { }

下面是介面的具體實現 (ConcreteFlyweight) , 並爲內部狀態增加記憶體空間 , ConcreteFlyweight 必須是可共用的 , 它保存的任何狀態都必須是內部 (intrinsic), 也就是說 ,ConcreteFlyweight 必須和它的應用環境場合無關。

public class ConcreteFlyweight implements Flyweight {
  
private IntrinsicState state;
  
  
public void operation( ExtrinsicState state )
  
{
       // 具體操作
   }

}

當然 , 並不是所有的 Flyweight 具體實現子類別都需要被共用的 , 所以還有另外一種不共用的 ConcreteFlyweight:

public class UnsharedConcreteFlyweight implements Flyweight {

   public void operation( ExtrinsicState state ) { }

}

Flyweight factory 負責維護一個 Flyweight( 存放內部狀態 ), 當用戶端請求一個共用 Flyweight, 這個 factory 首先搜索池中是否已經有可適用的 , 如果有 ,factory 只是簡單返回送出這個物件 , 否則 , 創建一個新的物件 , 加入到池中 , 再返回送出這個物件池

public class FlyweightFactory {
  
//Flyweight pool
  
private Hashtable flyweights = new Hashtable();

   public Flyweight getFlyweight( Object key ) {

     Flyweight flyweight = (Flyweight) flyweights.get(key);

     if( flyweight == null ) {
       // 産生新的
ConcreteFlyweight
      
flyweight = new ConcreteFlyweight();
      
flyweights.put( key, flyweight );
     }

     return flyweight;
   }
}

至此 ,Flyweight 模式的基本框架已經就緒 , 我們看看如何呼叫 :

FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( "Fred" );
Flyweight fly2 = factory.getFlyweight( "Wilma" );
......

從呼叫上看 , 好象是個純粹的 Factory 使用 , 但奧妙就在於 Factory 的內部設計上。

Flyweight 模式在 XML 等資料源中應用
我們上面已經提到 , 當大量從資料源中讀取字串 , 其中肯定有重復的 , 那麽我們使用 Flyweight 模式可以提高效率 , 以唱片 CD 爲例 , 在一個 XML 文件中 , 存放了多個 CD 的資料。

每個 CD 有三個欄位 :
1.
出片日期
(year)
2.
歌唱者姓名等資訊
(artist)
3.
唱片曲目 (title)

其中 , 歌唱者姓名有可能重復 , 也就是說 , 可能有同一個演唱者的多個不同時期 不同曲目的 CD 。我們將 " 歌唱者姓名 " 作爲可共用的 ConcreteFlyweight 。其他兩個欄位作爲 UnsharedConcreteFlyweight

首先看看資料源 XML 文件的內容 :


<?xml version="1.0"?>
<collection>

<cd>
<title>Another Green World</title>
<year>1978</year>
<artist>Eno, Brian</artist>
</cd>

<cd>
<title>Greatest Hits</title>
<year>1950</year>
<artist>Holiday, Billie</artist>
</cd>

<cd>
<title>Taking Tiger Mountain (by strategy)</title>
<year>1977</year>
<artist>Eno, Brian</artist>
</cd>

.......

</collection>


雖然上面舉例 CD 只有 3,CD 可看成是大量重復的小類別 , 因爲其中成分只有三個欄位 , 而且有重復的 ( 歌唱者姓名 )

CD 就是類似上面介面 Flyweight:


public class CD {

   private String title;
  
private int year;
  
private Artist artist;

   public String getTitle() {    return title;  
}
   public int getYear() {      return year;   
}
   public Artist getArtist() {      return artist;   
}

   public void setTitle(String t){     
title = t;}
  
public void setYear(int y){year = y;}
   public void setArtist(Artist a){artist = a;}

}

" 歌唱者姓名 " 作爲可共用的 ConcreteFlyweight:

public class Artist {

   // 內部狀態
   private String name;

   // note that Artist is immutable.
   String getName(){return name;}

   Artist(String n){
    
name = n;
   }

}

再看看 Flyweight factory, 專門用來製造上面的可共用的 ConcreteFlyweight:Artist

public class ArtistFactory {

   Hashtable pool = new Hashtable();

   Artist getArtist(String key){

     Artist result;
    
result = (Artist)pool.get(key);
     産生新的
Artist
    
if(result == null) {
      
result = new Artist(key);
      
pool.put(key,result);
      
    
}
   
return result;
  }

}

當你有幾千張甚至更多 CD,Flyweight 模式將節省更多空間 , 共用的 flyweight 越多 , 空間節省也就越大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值