Flyweight 享元模式 --對象結構型模式

1、意圖

        運用共享技術有效地支持大量細粒度的對象。


2、動機

         有些應用程序得益於在整個設計過程中採用對象技術,但簡單化的實現代價極大。

         例如,大多數文檔編輯器的實現都有文本格式化和編輯功能,這些功能在一定程度上是模塊化的。面向對象的文檔編輯器通常使用對象來表示嵌入的成分,例如表格和圖形。儘管用對象來表示文檔中的每個字符會極大地提高應用程序的靈活性,但是這些編輯器通常並不這樣做。字符和嵌入成分可以在繪製和格式化時統一處理,從而在不影響其他功能的情況下能對應用程序進行擴展,支持新的字符集。應用程序的對象結構可以模擬文檔的物理結構。下圖顯示了一個王丹編輯器怎樣使用對象來表示字符。


        但這種設計的確定在於代價太大。即使是一箇中等大小的文檔也可能要求成百上千的字符對象,這會耗費大量內存,產生難以接受的運行開銷。所以通常並不是對每個字符都用一個對象來表示的。Flyweight模式描述瞭如何共享對象,使得可以細粒度地使用它們而無需高昂的代價。

        flyweight是一個共享對象,它可以同時在多個場景(context)中使用,並且在每個場景中flyweight都可以作爲一個獨立的對象--這一點與非共享對象的實例沒有區別。flyweight不能對它所運行的場景作出任何假設,這裏的關鍵概念是內部狀態和外部狀態之間的區別。內部狀態存儲於fleweight中,它包含了獨立於flyweight場景的信息,這些信息使得flyweight可以被共享,而外部狀態取決於Flyweight場景,並根據場景而變化,因此不可共享。用戶對象負責在必要的時候將外部狀態傳遞給Flyweight。

        Flyweight模式對那些通常因爲數量太大而難以用對象來表示的概念或實體進行建模。例如,文檔編輯器可以爲字母表中的每一個字母創建一個flyweight。每個flyweight存儲一個字符代碼,但它在文檔中的位置和排版風格可以在字符出現時由正文排版算法和使用的格式化命令決定。字符代碼是內部狀態,而其他的信息則是外部狀態。

        邏輯上,文檔中的給定字符每次出現都有一個對象與其對應,如下圖所示。


        然而,物理上的每個字符共享一個flyweight對象,而這個對象出現在文檔結構中的不同地方。一個特定字符對象的每次出現都指向同一個實例,這個實例位於flyweight對象的共享池中。 這些對象的類結構如下圖所示。



        Glyph是圖形對象的抽象類,其中有些對象可能是flyweight。基於外部裝填的哪些操作將外部狀態作爲參量傳遞給它們。例如,Draw和Intersects在執行之前,必須知道glyph所在的場景,如下頁上圖所示。

        表示字母“a”的fleyweight只存儲相應的字符代碼;它不需要存儲字符的位置或字體。用戶提供與場景相關的信息,根據此信息flyweight繪出它自己。例如Row glyph知道它的子女應該在哪兒繪製自己才能保證它們是橫向排列的。因此Row glyph可以在繪製請求中向每一個子女傳遞它的位置。



        由於不同的字符對象數遠小於文檔中的字符數,因此,對象的總數遠小於一個初次執行的程序所使用的對象數目。對於一個所有字符都使用同樣的字體和顏色的文檔而言,不管這個文檔有多長,需要分配100個左右的字符對象(大約是ASCII字符集的數目)。由於大多數文檔使用的字體顏色組合不超過10種,實際應用中這一數目不會明顯增加。因此,對單個字符進行對象抽象是具有實際意義的。

        3、適用性

        Flyweight模式的有效性很大程度上取決於如何使用它以及在何處使用它。擋一下情況都成立時使用Flyweight模式:

        · 一個應用程序使用了大量的對象。

        · 完全由於使用大量的對象,造成很大的存儲開銷。

        · 對象的大多數狀態都可變爲外部狀態。

        · 如果刪除對象的外部狀態,那麼可以用相對較少的共享對象取代很多組對象。

        · 應用程序不依賴於對象標識。由於Flyweight對象可以被共享,對於概念上明顯有別的對象,標識測試將返回真值。


4、結構

        下面的對象圖說明了如何共享flyweight。


5、參與者

        · Flyweight(Glyph)

          -- 描述一個接口,通過這個接口flyweight可以接受並作用於外部狀態。

        · ConcreteFlyweight(Character)

          -- 實現Flyweight接口,並未內部狀態(如果有的話)增加存儲空間。

                concreteFlyweight對象必須是可共享的。它所存儲的狀態必須是內部的;即,它必須獨立於ConcreteFlyweight對象的場景。

        · UnsharedConcreteFlyweight(Row,Column)

          -- 並非所有的Flyweight子類都需要被共享。Flyweight接口使共享成爲可能,但它並不強制共享。在Flyweight對象結構的某些層次,UnsharedConcreteFlyweight對象通常將ConcreteFlyweight對象作爲子節點(Row和Column就是這樣)。

        · FlyweightFactory

          -- 創建管理flyweight對象。

          --確保合理地共享flyweight。當用戶請求一個flyweight時,FlyweightFactory對象提供一個已創建的實例或者創建一個(如果不存在的話)。

        ·Client

          -- 維持一個對flyweight的引用。

          -- 計算或存儲一個(多個)flyweight的外部狀態。


6、協作

        · flyweight執行時所需的狀態必定是內部的或外部的。內部狀態存儲於ConcreteFlyweight對象之中;而外部對象則由Client對象存儲或計算。當用戶調用flyweight對象的操作時,將該狀態傳遞給它。

        · 用戶不應直接對ConcreteFlyweight類進行實例化,而只能從FlyweightFacory對象得到ConcreteFlyweight對象,這可以保證對它們適當地進行共享。


7、效果

        使用Flyweight模式時,傳輸、查找和/或計算外部狀態都會產生運行時的開銷,尤其當flyweight原先被存儲爲內部狀態時。然而,空間上的節省抵消了這些開銷。共享的flyweight越多,空間節省也就越大。

        存儲節約由以下幾個因素決定:

        · 因爲共享,實例總數減少的數目;

        · 對象內部狀態的平均數目;

        · 內部狀態是計算的還是存儲的。


        共享的Flyweight越多,存儲節約也就越多。節約量隨着共享狀態的增多而增大。當對象使用大量的內部及外部狀態,並且外部狀態是計算出來的而非存儲的時候,節約量將達到最大。所以,可以用兩種方法來節約存儲:用共享減少內部狀態的消耗,用計算時間換取對外部狀態的存儲。

        Flyweight模式經常和Compsite模式結合起來表示一個層次式結構,這一層次式結構是一個共享葉節點的圖。共享的結果是,Flyweight的葉節點不能存儲指向父節點的指針。而父節點的指針將傳給Flyweight作爲它的外部狀態的一部分。這對於該層次結構中對象之間的相互通訊的方式將產生很大的影響。


8、實現

        在實現Flyweight模式時,注意以下幾點:

        1)刪除外部狀態    該模式的可用性在很大程度上取決於是否容易識別外部狀態並將它從共享對象中刪除。如果不同種類的外部狀態和共享前對象的數目相同的話,刪除外部狀態不會降低存儲消耗。理想的狀況是,外部狀態可以由一個單獨的對象結構計算得到,且該結構的存儲要求非常小。

        例如,在我們的文檔編輯器中,我們可以用一個單獨的結構存儲排版佈局信息,而不是存儲每一個字符對象的字體和類型信息,佈局圖保持了帶有相同拍板信息的字符的運行軌跡。當某字符繪製自己的時候,作爲繪圖遍歷的副作用它接收排版信息。因爲通常文檔使用的字體和類型數量有限,將該信息作爲外部信息來存儲,要比內部存儲高效得多。

        2)管理共享對象    因爲對象是共享的,用戶不能直接對它進行實例化,因此Flyweight-Factory可以幫助用戶查找某個特定到底Flyweight對象。FlyweightFactory對象經常使用關聯存儲幫助用戶查找感興趣的Flyweight對象。例如,在這個文檔編輯器一例中的Flyweight工廠就有一個以字符代碼爲索引的Flyweight表。管理程序根據所給的代碼返回相應的Flyweight,若不存在,則創建一個Flyweight。

        共享還意味着某種形式的引用計數和垃圾回收,這樣當一個Flyweight不再使用時,可以回收他的存儲空間。然而,當Flyweight的數目固定而且很小的時候(例如,用戶ACSII碼的Flyweight),這兩種操作都不必要。在這種情況下,Flyweight完全可以永久保存。


9、代碼示例

        略。。。。。。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章