1.1概述 運用共用技術有效地支持大量細粒度的對象。這就是享元模式的定義。 一個類中的成員變數表明該類所創建對象所具有的屬性,在某些程式設計中可能用一個類創建若幹個對象,但是發現這些對象的一個共同特點是它們有一部分屬性的取值必須是完全相同的。 例如,一個Car類,其類圖如下圖一所示: 圖一:Car類 ...
1.1概述
運用共用技術有效地支持大量細粒度的對象。這就是享元模式的定義。
一個類中的成員變數表明該類所創建對象所具有的屬性,在某些程式設計中可能用一個類創建若幹個對象,但是發現這些對象的一個共同特點是它們有一部分屬性的取值必須是完全相同的。
例如,一個Car類,其類圖如下圖一所示:
圖一:Car類
當用Car類創建若幹個的同型號的轎車時,比如創建若幹個“奧迪A6”轎車,要求這些轎車的height、width、length值都必須是相同的(轎車的屬性很多,屬於細粒度對象,而且轎車的很多屬性值是相同的,這裡只示意了height、width、length三個屬性),而color、power可以是不同的,就像很多“奧迪A6”轎車,他們的長度、高度和寬度都是相同的,但顏色和動力可能不同。
從創建對象的角度來看,我們面對的問題是:Car的每個對象的變數都各自占有不同的記憶體空間。這樣一來,Car創建的對象越多就越浪費記憶體,而且程式也無法保證Car類創建的多個對象所對應的height、width和length值是相同的或者禁止Car類對象隨意修改自己的height、width和lengh值。
現在重新設計Car類,由於要求Car類所創建的若幹個對象的height、width和length值都必須是相同的,因此沒有必要為每個Car對象的height、width和length分配不同的記憶體空間,現在將Car類中的height、width和length封裝到另一個CarData類中,CarData類的類圖如下圖二所示:
圖二:CarData類
現在,假設系統能保證向Car類的若幹個對象提供相同的CarData實例,即可以讓Car類的若幹個對象共用CarData類的一個實例,那麼就可以對Car類進行修改,修改後的Car類包含CarData實例,修改後的Car類類圖如下圖三所示:
圖三:修改後的Car類
這樣一來,Car類創建的若幹個對象的color、power都分配不同的記憶體空間,但是這些對象共用一個由系統提供的carData對象,節省了記憶體開銷。
1.2模式的結構
享元模式包括以下三種角色:
(1)享元介面(Flyweight):是一個介面,該介面定義了享元對外公開其內部數據的方法,以及享元接收外部數據的方法。
(2)具體享元(Concrete Flyweight):實現享元介面的類,該類的實例稱為享元對象,或簡稱享元。具體享元類的成員變數為享元對象的內部狀態,享元對象的內部狀態必須與所處的周圍環境無關,即要保證使用享元對象的應用程式無法更改享元的內部狀態,只有這樣才能使享元對象在系統中被共用。因為享元對象是用來共用的,所以不能允許用戶各自地使用具體享元類來創建對象,這樣就無法達到共用的目的,因為不同用戶用具體享元類創建的對象顯然是不同的,所以,具體享元類的構造方法必須是private的,其目的是不允許用戶程式直接使用具體享元類來創建享元對象,創建和管理享元對象有享元工廠負責。
(3)享元工廠(Flyweight Factory):享元工廠是一個類,該類的實例負責創建和管理享元對象,用戶或其他對象必須請求享元工廠為它得到一個享元對象。享元工廠可以通過一個散列表(也稱作共用池)來管理享元對象,當用戶程式或其他若幹個對象向享元工廠請求一個享元對象時,如果享元工廠的散列表中已有這樣的享元對象,享元工廠就提供這個享元對象給請求者,否則就創建一個享元對象添加到散列表中,同時將該享元對象提供給請求者。顯然,當若幹個用於或對象請求享元工廠提供一個享元對象時,第一個用戶獲得該享元對象的時間可能慢一些,但是後繼的用戶會較快地獲得這個享元對象。可以使用單例模式來設計這個享元工廠,即讓系統中只有一個享元工廠的實例,另外,為了讓享元工廠能生成享元對象,需要將具體享元類作為享元工廠的內部類。
享元模式結構的類圖如下圖四所示:
圖四:享元模式的類圖
1.3享元模式的優點
(1)使用享元可以節省記憶體的開銷,特別適合處理大量細粒度對象,這些對象的許多屬性值是相同的,而且一旦創建則不容許修改。
(2)享元模式中的享元可以使用方法的參數接受外部狀態中的數據,但外部狀態數據不會幹擾到享元中的內部數據,這就使享元可以在不同的環境中被共用。
1.4適合使用享元模式的情景
(1)一個應用程式使用大量的對象,這些對象之間部分屬性本質上是相同的,這時應使用享元來封裝相同的部分。
(2)對象的多數狀態都可改變為外部狀態,就可以考慮將這樣對象作為系統中的享元來使用。