設計模式之享元模式詳解(FlyWeight)

在面向對象程序設計過程中,有時會面臨要創建大量相同或相似對象實例的問題。創建那麼多的對象將會耗費很多的系統資源,它是系統性能提高的一個瓶頸。
例如,圍棋和五子棋中的黑白棋子,圖像中的座標點或顏色,局域網中的路由器、交換機和集線器,教室裏的桌子和凳子等。
這些對象有很多相似的地方,如果能把它們相同的部分提取出來共享,則能節省大量的系統資源,這就是享元模式的產生背景。

定義與優缺點

享元(Flyweight)模式的定義:運用共享技術來有効地支持大量細粒度對象的複用。它通過共享已經存在的又橡來大幅度減少需要創建的對象數量、避免大量相似類的開銷,從而提高系統資源的利用率。

享元模式的主要優點是:相同對象只要保存一份,這降低了系統中對象的數量,從而降低了系統中細粒度對象給內存帶來的壓力。

其主要缺點是:

  1. 爲了使對象可以共享,需要將一些不能共享的狀態外部化,這將增加程序的複雜性。
  2. 讀取享元模式的外部狀態會使得運行時間稍微變長。

結構

基本介紹

  1. 享元模式提出了兩個要求:細粒度和共享對象。這裏就涉及到內部狀態和外部狀態了,即將對象的信息分爲兩個部分:內部狀態和外部狀態
  2. 內部狀態指對象共享出來的信息,存儲在享元對象內部且不會隨環境的改變而改變
  3. 外部狀態指對象得以依賴的一個標記,是隨環境改變而改變的、不可共享的狀態。
  4. 舉個例子:圍棋理論上有 361 個空位可以放棋子,每盤棋都有可能有兩三百個棋子對象產生,因爲內存空間有限,一臺服務器很難支持更多的玩家玩圍棋遊戲,如果用享元模式來處理棋子,那麼棋子對象就可以減少到只有兩個實例,這樣就很好的解決了對象的開銷問題,棋子顏色就是棋子的內部狀態,棋子座標就是棋子的外部狀態。

原理類圖

在這裏插入圖片描述
模式的角色及職責

  1. 抽象享元角色(Flyweight):是所有的具體享元類的基類,爲具體享元規範需要實現的公共接口,非享元的外部狀態以參數的形式通過方法傳入。
  2. 具體享元(Concrete Flyweight)角色:實現抽象享元角色中所規定的接口。
  3. 非享元(Unsharable Flyweight)角色:是不可以共享的外部狀態,它以參數的形式注入具體享元的相關方法中。
  4. 享元工廠(Flyweight Factory)角色:負責創建和管理享元角色。當客戶對象請求一個享元對象時,享元工廠檢査系統中是否存在符合要求的享元對象,如果存在則提供給客戶;如果不存在的話,則創建一個新的享元對象。

模式的實現

//客戶端使用
public class Client {
    public static void main(String[] args) {
        FlyWeightFactory factory=new FlyWeightFactory();
        
        FlyWeight f01=factory.getFlyWeight("a");
        FlyWeight f02=factory.getFlyWeight("a");
        FlyWeight f03=factory.getFlyWeight("a");
        
        FlyWeight f11=factory.getFlyWeight("b");
        FlyWeight f12=factory.getFlyWeight("b");
        
        f01.operation(new UnSharedConcreteFlyWeight("第1次調用a。"));
        f02.operation(new UnSharedConcreteFlyWeight("第2次調用a。"));
        f03.operation(new UnSharedConcreteFlyWeight("第3次調用a。"));
        f11.operation(new UnSharedConcreteFlyWeight("第1次調用b。"));
        f12.operation(new UnSharedConcreteFlyWeight("第2次調用b。"));
    }
}
//抽象享元角色
public interface FlyWeight {
    public void operation(UnSharedConcreteFlyWeight state);
}
//具體享元角色
public class ConcreteFlyWeight implements FlyWeight {

    private String key;

    ConcreteFlyWeight(String key) {
        this.key = key;
        System.out.println("具體享元"+key+"被創建");
    }

    @Override
    public void operation(UnSharedConcreteFlyWeight outState) {
        System.out.print("具體享元"+key+"被調用,");
        System.out.println("非享元信息是:"+outState.getInfo());
    }
}
//非享元角色
public class UnSharedConcreteFlyWeight {
    private String info;

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    UnSharedConcreteFlyWeight(String info) {
        this.info = info;
    }
}
//享元工廠角色
public class FlyWeightFactory {
    private HashMap<String,FlyWeight> flyWeights = new HashMap<>();

    public FlyWeight getFlyWeight(String key) {
        FlyWeight flyWeight = flyWeights.get(key);
        if (flyWeight != null) {
            System.out.println("具體享元"+key+"已經存在,被成功獲取!");
        } else {
            flyWeight = new ConcreteFlyWeight(key);
            flyWeights.put(key,flyWeight);
        }
        return flyWeight;
    }
}

應用場景

  1. 統中存在大量相同或相似的對象,這些對象耗費大量的內存資源。
  2. 大部分的對象可以按照內部狀態進行分組,且可將不同部分外部化,這樣每一個組只需保存一個內部狀態。
  3. 由於享元模式需要額外維護一個保存享元的數據結構,所以應當在有足夠多的享元實例時才值得使用享元模式。

模式擴展

在前面介紹的享元模式中,其結構圖通常包含可以共享的部分和不可以共享的部分。在實際使用過程中,有時候會稍加改變,即存在兩種特殊的享元模式:單純享元模式和複合享元模式,下面分別對它們進行簡單介紹。
(1) 單純享元模式,這種享元模式中的所有的具體享元類都是可以共享的,不存在非共享的具體享元類,其結構圖如下圖所示。
單純享元模式的結構圖
(2) 複合享元模式,這種享元模式中的有些享元對象是由一些單純享元對象組合而成的,它們就是複合享元對象。雖然複合享元對象本身不能共享,但它們可以分解成單純享元對象再被共享,其結構圖如下圖所示。
複合享元模式的結構圖

享元模式在 JDK-Interger 的應用源碼分析

public class FlyWeight {
	public static void main(String[] args) {
		//如果  Integer.valueOf(x) x 在	-128 --- 127 直接,就是使用享元模式返回,如果不在範圍類,則仍然 new
		
		//小結:
		//1. 在 valueOf 方法中,先判斷值是否在 IntegerCache 中,如果不在,就創建新的 Integer(new),  否則,就直接從 緩存池返回
		//2. valueOf 方法,就使用到享元模式
		//3. 如果使用 valueOf 方法得到一個 Integer 實例,範圍在 -128 - 127  ,執行速度比 new 快
		
		Integer x = Integer.valueOf(127); // 得到 x 實例,類型 Integer 
		Integer y = new Integer(127); // 得 到 y 實 例 , 類 型 Integer
		Integer z = Integer.valueOf(127);//..
		Integer w = new Integer(127);
		
		System.out.println(x.equals(y)); // 大小,true 
		System.out.println(x == y ); //	false 
		System.out.println(x == z ); // true 
		System.out.println(w == x ); // false 
		System.out.println(w == y ); // false
		
		Integer x1 = Integer.valueOf(200); //不在IntegerCache中,創建新的 Integer(new)
		Integer x2 = Integer.valueOf(200);//不在IntegerCache中,創建新的 Integer(new)
	
		System.out.println("x1==x2" + (x1 == x2)); // false
	}
}

參考文章:
享元模式(詳解版)

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