面向對象可以非常方便的解決一些擴展性的問題,但是在這個過程中系統務必會產生一些類或者對象,如果系統中存在對象的個數過多時,將會導致系統的性能下降。對於這樣的問題解決最簡單直接的辦法就是減少系統中對象的個數。
享元模式提供了一種解決方案,使用共享技術實現相同或者相似對象的重用。也就是說實現相同或者相似對象的代碼共享。
一、 模式定義
所謂享元模式就是運行共享技術有效地支持大量細粒度對象的復用。系統使用少量對象,而且這些都比較相似,狀態變化小,可以實現對象的多次復用。
共享模式是支持大量細粒度對象的復用,所以享元模式要求能夠共享的對象必須是細粒度對象。
在了解享元模式之前我們先要了解兩個概念:內部狀態、外部狀態。
內部狀態:在享元對象內部不隨外界環境改變而改變的共享部分。
外部狀態:隨着環境的改變而改變,不能夠共享的狀態就是外部狀態。
由於享元模式區分了內部狀態和外部狀態,所以我們可以通過設置不同的外部狀態使得相同的對象可以具備一些不同的特性,而內部狀態設置為相同部分。在我們的程序設計過程中,我們可能會需要大量的細粒度對象來表示對象,如果這些對象除了幾個參數不同外其他部分都相同,這個時候我們就可以利用享元模式來大大減少應用程序當中的對象。如何利用享元模式呢?這里我們只需要將他們少部分的不同的部分當做參數移動到類實例的外部去,然后再方法調用的時候將他們傳遞過來就可以了。這里也就說明了一點:內部狀態存儲於享元對象內部,而外部狀態則應該由客戶端來考慮。
二、 模式結構
下圖是享元模式的UML結構圖
享元模式存在如下幾個角色:
Flyweight: 抽象享元類。所有具體享元類的超類或者接口,通過這個接口,Flyweight可以接受並作用於外部專題
ConcreteFlyweight: 具體享元類。指定內部狀態,為內部狀態增加存儲空間。
UnsharedConcreteFlyweight: 非共享具體享元類。指出那些不需要共享的Flyweight子類。
FlyweightFactory: 享元工廠類。用來創建並管理Flyweight對象,它主要用來確保合理地共享Flyweight,當用戶請求一個Flyweight時,FlyweightFactory就會提供一個已經創建的Flyweight對象或者新建一個(如果不存在)。
享元模式的核心在於享元工廠類,享元工廠類的作用在於提供一個用於存儲享元對象的享元池,用戶需要對象時,首先從享元池中獲取,如果享元池中不存在,則創建一個新的享元對象返回給用戶,並在享元池中保存該新增對象。
public class FlyweightFactory{ private HashMap flyweights = new HashMap(); public Flyweight getFlyweight(String key){ if(flyweights.containsKey(key)){ return (Flyweight)flyweights.get(key); } else{ Flyweight fw = new ConcreteFlyweight(); flyweights.put(key,fw); return fw; } } }
三、 模式實現
場景:假如我們有一個繪圖的應用程序,通過它我們可以出繪制各種各樣的形狀、顏色的圖形,那么這里形狀和顏色就是內部狀態了,通過享元模式我們就可以實現該屬性的共享了。如下:
首先是形狀類:Shape.java。它是抽象類,只有一個繪制圖形的抽象方法。
public abstract class Shape { public abstract void draw(); }
然后是繪制圓形的具體類。Circle.java
public class Circle extends Shape{ private String color; public Circle(String color){ this.color = color; } public void draw() { System.out.println("畫了一個" + color +"的圓形"); } }
再是享元工廠類。FlyweightFactory
public class FlyweightFactory{ static Map<String, Shape> shapes = new HashMap<String, Shape>(); public static Shape getShape(String key){ Shape shape = shapes.get(key); //如果shape==null,表示不存在,則新建,並且保持到共享池中 if(shape == null){ shape = new Circle(key); shapes.put(key, shape); } return shape; } public static int getSum(){ return shapes.size(); } }
public class Client { public static void main(String[] args) { Shape shape1 = FlyweightFactory.getShape("紅色"); shape1.draw(); Shape shape2 = FlyweightFactory.getShape("灰色"); shape2.draw(); Shape shape3 = FlyweightFactory.getShape("綠色"); shape3.draw(); Shape shape4 = FlyweightFactory.getShape("紅色"); shape4.draw(); Shape shape5 = FlyweightFactory.getShape("灰色"); shape5.draw(); Shape shape6 = FlyweightFactory.getShape("灰色"); shape6.draw(); System.out.println("一共繪制了"+FlyweightFactory.getSum()+"中顏色的圓形"); } }
運行結果
四、 模式優缺點
優點
1、享元模式的優點在於它能夠極大的減少系統中對象的個數。
2、享元模式由於使用了外部狀態,外部狀態相對獨立,不會影響到內部狀態,所以享元模式使得享元對象能夠在不同的環境被共享。
缺點
1、由於享元模式需要區分外部狀態和內部狀態,使得應用程序在某種程度上來說更加復雜化了。
2、為了使對象可以共享,享元模式需要將享元對象的狀態外部化,而讀取外部狀態使得運行時間變長。
五、 模式適用場景
1、如果一個系統中存在大量的相同或者相似的對象,由於這類對象的大量使用,會造成系統內存的耗費,可以使用享元模式來減少系統中對象的數量。
2、對象的大部分狀態都可以外部化,可以將這些外部狀態傳入對象中。
六、 模式總結
1、享元模式可以極大地減少系統中對象的數量。但是它可能會引起系統的邏輯更加復雜化。
2、享元模式的核心在於享元工廠,它主要用來確保合理地共享享元對象。
3、內部狀態為不變共享部分,存儲於享元享元對象內部,而外部狀態是可變部分,它應當油客戶端來負責。



