Flyweight 定義 :
避免大量擁有相同內容的小類別的開銷 ( 如耗費記憶體 ), 使大家共用一個類別 ( 元類別 ) 。
爲什麽使用 ?
面向物件語言的原則就是一切都是物件 , 但是如果真正使用起來 , 有時物件數可能顯得很龐大 , 比如 , 文字處理軟體 , 如果以每個文字都作爲一個物件 , 幾千個字 , 物件數就是幾千 , 無疑耗費記憶體 , 那麽我們還是要 " 求同存異 ", 找出這些物件群的共同點 , 設計一個元類別 , 封裝可以被共用的類別 , 另外 , 還有一些特性是取決於應用 (context), 是不可共用的 , 這也 Flyweight 中兩個重要概念內部狀態 intrinsic 和外部狀態 extrinsic 之分。
說白點 , 就是先捏一個的原始模型 , 然後隨著不同場合和環境 , 再産生各具特徵的具體模型 , 很顯然 , 在這裏需要産生不同的新物件 , 所以 Flyweight 模式中常出現 Factory 模式。 Flyweight 的內部狀態是用來共用的 ,Flyweight factory 負責維護一個 Flyweight pool( 模式池 ) 來存放內部狀態的物件。
Flyweight 模式是一個提高程式效率和性能的模式 , 會大大加快程式的運行速度。應用場合很多 : 比如你要從一個資料庫中讀取一系列字串 , 這些字串中有許多是重復的 , 那麽我們可以將這些字串儲存在 Flyweight 池 (pool) 中。
如何使用 ?
我們先從 Flyweight 抽象介面開始 :
public interface Flyweight { public void operation( ExtrinsicState state ); } // 用於本模式的抽象資料類型 ( 自行設計 ) |
下面是介面的具體實現 (ConcreteFlyweight) , 並爲內部狀態增加記憶體空間 , ConcreteFlyweight 必須是可共用的 , 它保存的任何狀態都必須是內部 (intrinsic), 也就是說 ,ConcreteFlyweight 必須和它的應用環境場合無關。
public class ConcreteFlyweight implements Flyweight { } |
當然 , 並不是所有的 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 ) { 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> <cd> <cd> ....... </collection> |
雖然上面舉例 CD 只有 3 張 ,CD 可看成是大量重復的小類別 , 因爲其中成分只有三個欄位 , 而且有重復的 ( 歌唱者姓名 ) 。
CD 就是類似上面介面 Flyweight:
public class CD { private String title; |
將 " 歌唱者姓名 " 作爲可共用的 ConcreteFlyweight:
public class Artist { // 內部狀態 // note that Artist is immutable. Artist(String n){ |
再看看 Flyweight factory, 專門用來製造上面的可共用的 ConcreteFlyweight:Artist
public class ArtistFactory { Hashtable pool = new Hashtable(); Artist result; |
當你有幾千張甚至更多 CD 時 ,Flyweight 模式將節省更多空間 , 共用的 flyweight 越多 , 空間節省也就越大。