一、概念 享元模式是對象的結構模式,它以共用的方式高效的支持大量的細粒度對象,減少對象的數量,並達到節約記憶體的目的。 享元對象能夠做到共用的關鍵,主要是區分了內部狀態和外部狀態,內部狀態是對象是在建立時就已確定了,且它不隨環境的改變而有所不同,所以這些內部狀態就可以共用,而外部狀態是會隨著環境的變化 ...
一、概念
享元模式是對象的結構模式,它以共用的方式高效的支持大量的細粒度對象,減少對象的數量,並達到節約記憶體的目的。
享元對象能夠做到共用的關鍵,主要是區分了內部狀態和外部狀態,內部狀態是對象是在建立時就已確定了,且它不隨環境的改變而有所不同,所以這些內部狀態就可以共用,而外部狀態是會隨著環境的變化而會改變的,不可以共用。所以外部狀態必須由客戶端保存,當需要時可以傳給享元對象。
二、模式動機
當一個系統對於同一個對象類型,有大量的對象實例 ,且這些對象實例裡面的狀態大部分都可以外部化,而對一些不可變的相同內部狀態一組實例,就可以用一個對象代替。這樣就可以減少對角的數量,從而達到節約記憶體目的。
三、模式的結構
角色分析:
FlyWeight:享元介面,通過這個介面Flyweight可以接受並作用於外部狀態。通過這個介面傳入外部的狀態,在享元對象的方法處理過程中可能會使用到這些數據。
ConcreteFlyWeight:具體的享元對象,這些對象必須是可以共用的,需要封裝Flyweight的內部狀態。
UnsharedConcreteFlyweight:非共用的享元實例對象,並非所有的Flyweight實現對象都需要共用,非共用的享元實現對象通常是共用的享元實例對象的組合。
FlyWeightFactory:主要用來創建並管理共用享元對象,並對外提供訪問共用享元對象的介面,它內部往往有一個共用享元對象的實例池,通過這個實例池來實現享元對象的共用。
Client:享元客戶端,主要的工作是維持一個對Flyweight對象的引用,通過FlyWeightFactory獲取享元對象,並將客戶端存儲的外部狀態作用於享元對象。
代碼樣例如下:
package flyweight.sample; /** * 抽像享元角色,所有具體享元角色的超類,通過這個角色享元接收並作用於外部狀態 * @ClassName: FlyWeight * @author beteman6988 * @date 2018年3月31日 上午7:50:48 * */ public interface FlyWeight { /** * 具體業務邏輯 * @Title: operation * @param @param extrinsicSate 外部狀態 * @return void * @throws */ public void operation(String extrinsicSate); } /** * 享元對象:可以共用的享元對象 * @ClassName: ConcreteFlyWeight * @author beteman6988 * @date 2018年3月31日 上午7:56:13 * */ public class ConcreteFlyWeight implements FlyWeight { private String intrinsicState ; //不依賴於環境改變而改變的內部狀態 /** * 構造方法,傳入享元對象的內部狀態 * @param intrinsicState:內部狀態 */ public ConcreteFlyWeight(String intrinsicState) { super(); this.intrinsicState = intrinsicState; } /** * 具體業務邏輯 * @Title: operation * @param @param extrinsicSate 外部狀態 * @return void * @throws */ @Override public void operation(String extrinsicSate) { //具體業務邏輯 } } import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * 不需要共用的複合享元對象 * * @ClassName: UnsharedConcreteFlyWeight * @author beteman6988 * @date 2018年3月31日 上午8:05:55 * */ public class UnsharedConcreteFlyWeight implements FlyWeight { //享元集合 ,主鍵:代表共用享元的主鍵 值:共用享元主鍵代表的共用享元 private Map<String,FlyWeight> flies = new HashMap<String,FlyWeight>(); /** * 往複合享元對象中添加共用享元 * @Title: add * @param @param intrinsticState:內部狀態(不一定必須是內部狀態,只要是代表該共用享元的主鍵即可) * @param @param oneFlyWeight * @return void * @throws */ public void add(String intrinsticState,FlyWeight oneFlyWeight) { this.flies.put(intrinsticState,oneFlyWeight); } /** * 作用於所有無享元的具體業務邏輯 * @Title: operation * @param @param extrinsicSate 外部狀態 * @return void * @throws */ @Override public void operation(String extrinsicSate) { //具體業務邏輯 } public String toString() { for(Iterator it=flies.entrySet().iterator();it.hasNext();) { Map.Entry entry=(Entry) it.next(); System.out.println(entry.getValue()); } return null; } } import java.util.HashMap; import java.util.List; import java.util.Map; /** * 提供共用享元對象或複合享元對象的工廠 * * @ClassName: FlyWeightFactory * @author beteman6988 * @date 2018年3月31日 上午8:24:14 * */ public class FlyWeightFactory { // 共用享元的實例池 ,主鍵:代表共用享元的主鍵 值:共用享元主鍵代表的共用享元 private Map<String, FlyWeight> flies = new HashMap<String, FlyWeight>(); /** * 返回共用享元對象的工廠方法 * @Title: factory * @param @param intrinsticState :內部狀態 * @param @return * @return FlyWeight :共用享元實例 * @throws */ public FlyWeight factory(String intrinsticState) { // 從實例池獲取需要的享元對象 FlyWeight flyWeight = this.flies.get(intrinsticState); if (null == flyWeight) { // 如果獲取不到則創建享元對象 flyWeight = new ConcreteFlyWeight(intrinsticState); this.flies.put(intrinsticState, flyWeight); // 將創建的享元對象添加到實例池 } return flyWeight; } /** * 返回不共用的複合享元實例 * @Title: factory * @param @param intrinsticStates * @param @return * @return FlyWeight * @throws */ public FlyWeight factory(List<String> intrinsticStates) { UnsharedConcreteFlyWeight unsharedFlyWeight=new UnsharedConcreteFlyWeight(); //創建不共用的複合享元 for(String intrinsticState:intrinsticStates) { unsharedFlyWeight.add(intrinsticState, this.factory(intrinsticState)); } return unsharedFlyWeight; } } import java.util.ArrayList; import java.util.List; public class Client { public static void main(String[] args) { FlyWeightFactory factory =new FlyWeightFactory(); List list=new ArrayList(); list.add("a"); list.add("a"); FlyWeight three=factory.factory(list); System.out.println(three); FlyWeight one=factory.factory("a"); FlyWeight two=factory.factory("a"); System.out.println(one); System.out.println(two); } }View Code
代碼運行結果如下:
flyweight.sample.ConcreteFlyWeight@15db9742
null
flyweight.sample.ConcreteFlyWeight@15db9742
flyweight.sample.ConcreteFlyWeight@15db9742
通過結果可以看出,三個對象的hashcode相同,說明是同一個對象。
四、模式樣例
通過分析Integer.valueOf(int i)來分析JDK(JDK1,8.0.131)享元設計模式,首先分析Integer這個類是否可以做為一個享元的實現類,如下圖:
可以看出這個類的內部狀態為final的value,且這個值是通過構造函數傳入,因為它是final int,所以它的值是不隨環境的改變而受到影響的,所以Integer類可以做為享元對象的實現類來使用。
再來看valueOf(int i)函數:
可以看出如果傳入的值>=IntegerCache.low且<=IntegerCache.high ,那麼就直接從IntegerCache.cache[i + (-IntegerCache.low)]里直接去取對象,而不是新建對象,從而實現對象的共用。
再來看一下IntegerCache這個類,如下圖:
可以看出它是Integer類的一個靜態私有內部類,它有一個static final Integer cache[]的成員,且這成員變數在類載入時通過靜態初始化塊(語句2)將256(從上面的代碼可以看出 是從 -128到127 共256個Integer對象)個Integer對象初始化到了cache[]數組裡面,只要Integer類通過靜態方法public static Integer valueOf(int i) 就可以直接從這個cache[]裡面取到對應int值的Integer對象(前提是int i值的範圍也是-128到127 ,但是這個緩存數組的大小也是可以改變的,通過語句3 ,在JDK的參數裡面設置-Djava.lang.Integer.IntegerCache.high=整數值,就可以改變大小,但是這個值不能小於127,如果小於127會取預設的127 )。
五、與其它模式的關係
享元模式與單例模式:這兩個模式可以組合使用。通常情況下,享元模式中的享元工廠可以實現成為單例模式。
享元模式與組合模式:這兩個模式也是可以組合使用的。在享元模式中,存在不需要共用的實現,這些不需要共用的享元通常是對共用的享元對象的組合對象。也就是說,享元模式通常會和組合模式組合使用,來實現更複雜的對象層次結構。