1. 程式人生 > >Java設計模式(十二)----享元模式

Java設計模式(十二)----享元模式

享元模式
一、 概念
二、享元的用途
三、結構和分類
1、單純享元模式 
2、複合享元模式
四、享元模式的優缺點

一、概念

 Flyweight在拳擊比賽中指最輕量級,即“蠅量級”或“雨量級”,這裡選擇使用“享元模式”的意譯,是因為這樣更能反映模式的用意。享元模式是物件的結構模式。享元模式以共享的方式高效地支援大量的細粒度物件。
也就是說在一個系統中如果有多個相同的物件,那麼只共享一份就可以了,不必每個都去例項化一個物件。

二、享元的用途

在java應用中,會出現許多String a=”123”,String b=”123”之類的String型別的變數,如果只是小應用,到還好,假設是一個龐大的系統,有好多處都需要用定義String a=”223”,那開銷可想而知,而JDK的開發者自然想到了這點,採用了享元模式解決建立大量相同String變數帶來的開銷問題

在JAVA語言中,String型別就是使用了享元模式。String物件是final型別,物件一旦建立就不可改變。在JAVA中字串常量 都是存在常量池中的,JAVA會確保一個字串常量在常量池中只有一個拷貝。String a=”abc”,其中”abc”就是一個字串常量。

public class Test {

    public static void main(String[] args) {

        String a = "abc";
        String b = "abc";
        System.out.println(a==b);

    }
}

  上面的例子中結果為:true ,這就說明a和b兩個引用都指向了常量池中的同一個字串常量”abc”。這樣的設計避免了在建立N多相同物件時所產生的不必要的大量的資源消耗。

三、結構和分類
享元模式採用一個共享來避免大量擁有相同內容物件的開銷。這種開銷最常見、最直觀的就是記憶體的損耗。享元物件能做到共享的關鍵是區分內蘊狀態(Internal State)和外蘊狀態(ExternalState)。

一個內蘊狀態是儲存在享元物件內部的,並且是不會隨環境的改變而有所不同。因此,一個享元可以具有內蘊狀態並可以共享。

一個外蘊狀態是隨環境的改變而改變的、不可以共享的。享元物件的外蘊狀態必須由客戶端儲存,並在享元物件被建立之後,在需要使用的時候再傳入到享元物件內部。外蘊狀態不可以影響享元物件的內蘊狀態,它們是相互獨立的。

享元模式可以分成單純享元模式和複合享元模式兩種形式。


1、單純享元模式  
在單純的享元模式中,所有的享元物件都是可以共享的。

這裡寫圖片描述

角色如下:

●抽象享元(Flyweight)角色 :給出一個抽象介面,以規定出所有具體享元角色需要實現的方法。

●具體享元(ConcreteFlyweight)角色:實現抽象享元角色所規定出的介面。如果有內蘊狀態的話,必須負責為內蘊狀態提供儲存空間。

●享元工廠(FlyweightFactory)角色 :本角色負責建立和管理享元角色。本角色必須保證享元物件可以被系統適當地共享。當一個客戶端物件呼叫一個享元物件的時候,享元工廠角色會檢查系統中是否 已經有一個符合要求的享元物件。如果已經有了,享元工廠角色就應當提供這個已有的享元物件;如果系統中沒有一個適當的享元物件的話,享元工廠角色就應當建立一個合適的享元物件。

原始碼:
(下面的例子外蘊狀態可以看做一個點菜的人 內蘊狀態可以看做一個菜名,點菜的人一般經常變 但是菜的種類是固定的)

抽象享元角色類

//抽象享元角色類
public interface Flyweight {
     //一個示意性方法,引數state是外蘊狀態
    public void operation(String state);
}

 具體享元角色類ConcreteFlyweight有一個內蘊狀態,在本例中一個Character型別的intrinsicState屬性代表,它的值應當在享元物件被建立時賦予。所有的內蘊狀態在物件建立之後,就不會再改變了。
如果一個享元物件有外蘊狀態的話,所有的外部狀態都必須儲存在客戶端,在使用享元物件時,再由客戶端傳入享元物件。這裡只有一個外蘊狀態,operation()方法的引數state就是由外部傳入的外蘊狀態。

//具體享元角色類
public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState = null;//內蘊狀態
    /**
     * 建構函式,內蘊狀態作為引數傳入
     * 
     * @param intrinsicState
     */
    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    /**
     * 外蘊狀態作為引數傳入方法中,改變方法的行為, 但是並不改變物件的內蘊狀態。
     */
    @Override
    public void operation(String state) {
        System.out.println("內蘊狀態= " + this.intrinsicState);
        System.out.println("外蘊狀態 = " + state);
    }

}

享元工廠角色類,必須指出的是,客戶端不可以直接將具體享元類例項化,而必須通過一個工廠物件,利用一個factory()方法得到享元物件。一般而言,享元工廠物件在整個系統中只有一個,因此也可以使用單例模式。
當客戶端需要單純享元物件的時候,需要呼叫享元工廠的factory()方法,並傳入所需的單純享元物件的內蘊狀態,由工廠方法產生所需要的享元物件。

//享元工廠角色類
public class FlyweightFactory {
    // 一個用來存所有享元物件的集合 String表示物件的鍵的型別 ->內蘊狀態 ;Flyweight表示物件值的型別
    private Map<String, Flyweight> files = new HashMap<String, Flyweight>();

    public Flyweight factory(String intrinsicState) {
        // 先從快取中查詢物件
        Flyweight fly = files.get(intrinsicState);
        if (fly == null) {
            // 如果物件不存在則建立一個新的Flyweight物件
            fly = new ConcreteFlyweight(intrinsicState);
            // 把這個新的Flyweight物件新增到快取中
            files.put(intrinsicState, fly);
        }
        return fly;
    }
    //得到存物件的集合的長度
    public int getFlyWeightSize() {
        return files.size();
    }

}
public class Client {

    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();
        Flyweight fly1 = factory.factory(new String("辣椒炒肉"));
        fly1.operation("湯高點菜");

        Flyweight fly2 = factory.factory(new String("牛肉"));
        fly2.operation("周思遠點菜");

        Flyweight fly3 = factory.factory(new String("辣椒炒肉"));
        fly3.operation("湯高點菜");

        System.out.println(fly1==fly3);
        System.out.println("被點不同的菜的個數"+factory.getFlyWeightSize());
    }

}

結果:
內蘊狀態= 辣椒炒肉
外蘊狀態 = 湯高點菜
內蘊狀態= 牛肉
外蘊狀態 = 周思遠點菜
內蘊狀態= 辣椒炒肉
外蘊狀態 = 湯高點菜
true
被點不同的菜的個數2

雖然客戶端申請了三個享元物件,但是實際建立的享元物件只有兩個,這就是共享的含義

2、複合享元模式
  在單純享元模式中,所有的享元物件都是單純享元物件,也就是說都是可以直接共享的。還有一種較為複雜的情況,將一些單純享元使用合成模式加以複合,形成複合享元物件。這樣的複合享元物件本身不能共享,但是它們可以分解成單純享元物件,而後者則可以共享。

這裡寫圖片描述
  
  
角色如下:
●抽象享元(Flyweight)角色 :給出一個抽象介面,以規定出所有具體享元角色需要實現的方法。
  
●複合享元(ConcreteCompositeFlyweight)角色 :複合享元角色所代表的物件是不可以共享的,但是一個複合享元物件可以分解成為多個本身是單純享元物件的組合。複合享元角色又稱作不可共享的享元物件。
  
●享元工廠(FlyweightFactory)角色 :本角 色負責建立和管理享元角色。本角色必須保證享元物件可以被系統適當地共享。當一個客戶端物件呼叫一個享元物件的時候,享元工廠角色會檢查系統中是否已經有 一個符合要求的享元物件。如果已經有了,享元工廠角色就應當提供這個已有的享元物件;如果系統中沒有一個適當的享元物件的話,享元工廠角色就應當建立一個 合適的享元物件。
(下面的例子外蘊狀態可以看做一個點菜的人 內蘊狀態可以看做一個菜名,點菜的人一般經常變 但是菜的種類是固定的)

原始碼
抽象享元角色類

//抽象享元角色類
public interface Flyweight {
     //一個示意性方法,引數state是外蘊狀態
    public void operation(String state);
}

 
具體享元角色類

///具體享元角色類
public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState = null;//內蘊狀態
    /**
     * 建構函式,內蘊狀態作為引數傳入
     * 
     * @param intrinsicState2
     */
    public ConcreteFlyweight(String intrinsicState2) {
        this.intrinsicState = intrinsicState2;
    }

    /**
     * 外蘊狀態作為引數傳入方法中,改變方法的行為, 但是並不改變物件的內蘊狀態。
     */
    @Override
    public void operation(String state) {
        System.out.println("內蘊狀態= " + this.intrinsicState);
        System.out.println("外蘊狀態 = " + state);
    }

}

複合享元物件是由單純享元物件通過複合而成的,因此它提供了add()這樣的聚集管理方法。由於一個複合享元物件具有不同的聚集元素,這些聚集元素在複合享元物件被建立之後加入,這本身就意味著複合享元物件的狀態是會改變的,因此複合享元物件是不能共享的。
  複合享元角色實現了抽象享元角色所規定的介面,也就是operation()方法,這個方法有一個引數,代表複合享元物件的外蘊狀態。一個複合 享元物件的所有單純享元物件元素的外蘊狀態都是與複合享元物件的外蘊狀態相等的;而一個複合享元物件所含有的單純享元物件的內蘊狀態一般是不相等的,不然 就沒有使用價值了。
  

//複合享元角色
public class ConcreteCompositeFlyweight implements Flyweight {

    private Map<String, Flyweight> files = new HashMap<String, Flyweight>();

    /**
     * 增加一個新的單純享元物件到聚集中
     */
    public void add(String intrinsicState, Flyweight fly) {
        files.put(intrinsicState, fly);
    }

    /**
     * 外蘊狀態作為引數傳入到方法中
     */
    @Override
    public void operation(String state) {
        Flyweight fly = null;
        for (String intrinsicState : files.keySet()) {
            fly = files.get(intrinsicState);
            fly.operation(state);
        }

    }

}

享元工廠角色提供兩種不同的方法,一種用於提供單純享元物件,另一種用於提供複合享元物件。

//享元工廠角色類
public class FlyweightFactory {
    // 一個用來存所有享元物件的集合 String表示物件的鍵的型別 ->內蘊狀態 ;Flyweight表示物件值的型別
    private Map<String, Flyweight> files = new HashMap<String, Flyweight>();

    /**
     * 複合享元工廠方法
     */
    public Flyweight factory(List<String> compositeState) {
        ConcreteCompositeFlyweight compositeFly = new ConcreteCompositeFlyweight();

        for (String intrinsicState : compositeState) {
            compositeFly.add(intrinsicState, this.factory(intrinsicState));
        }

        return compositeFly;
    }

    /**
     * 單純享元工廠方法
     */
    public Flyweight factory(String intrinsicState) {
        // 先從快取中查詢物件
        Flyweight fly = files.get(intrinsicState);
        if (fly == null) {
            // 如果物件不存在則建立一個新的Flyweight物件
            fly = new ConcreteFlyweight(intrinsicState);
            // 把這個新的Flyweight物件新增到快取中
            files.put(intrinsicState, fly);
        }
        return fly;
    }

    // 得到存物件的集合的長度
    public int getFlyWeightSize() {
        return files.size();
    }

}
public class Client {
    public static void main(String[] args) {
        List<String> compositeState = new ArrayList<String>();
        compositeState.add("辣椒炒肉");
        compositeState.add("牛肉");
        compositeState.add("雞肉");
        compositeState.add("辣椒炒肉");
        compositeState.add("牛肉");

        FlyweightFactory flyFactory = new FlyweightFactory();
        Flyweight compositeFly1 = flyFactory.factory(compositeState);
        Flyweight compositeFly2 = flyFactory.factory(compositeState);
        compositeFly1.operation("湯高點菜");//外蘊狀態是同一個
        System.out.println();
        compositeFly2.operation("周思遠點菜");

        System.out.println("---------------------------------");
        System.out.println("複合享元模式是否可以共享物件:" + (compositeFly1 == compositeFly2));

        String state = "牛肉";
        Flyweight fly1 = flyFactory.factory(state);
        Flyweight fly2 = flyFactory.factory(state);
        System.out.println("單純享元模式是否可以共享物件:" + (fly1 == fly2));
    }

}

結果:
內蘊狀態= 辣椒炒肉
外蘊狀態 = 湯高點菜
內蘊狀態= 雞肉
外蘊狀態 = 湯高點菜
內蘊狀態= 牛肉
外蘊狀態 = 湯高點菜

內蘊狀態= 辣椒炒肉
外蘊狀態 = 周思遠點菜
內蘊狀態= 雞肉
外蘊狀態 = 周思遠點菜
內蘊狀態= 牛肉
外蘊狀態 = 周思遠點菜

複合享元模式是否可以共享物件:false
單純享元模式是否可以共享物件:true

從執行結果可以看出,一個複合享元物件的所有單純享元物件元素的外蘊狀態都是與複合享元物件的外蘊狀態相等的。即外運狀態都等於湯高點菜或者周思遠點菜。

從執行結果可以看出,一個複合享元物件所含有的單純享元物件的內蘊狀態一般是不相等的。即內蘊狀態分別為各自的菜名。

從執行結果可以看出,複合享元物件是不能共享的。即使用相同的物件compositeState通過工廠分別兩次創建出的物件不是同一個物件。

從執行結果可以看出,單純享元物件是可以共享的。即使用相同的物件state通過工廠分別兩次創建出的物件是同一個物件。

四、享元模式的優缺點

  享元模式的優點在於它大幅度地降低記憶體中物件的數量。但是,它做到這一點所付出的代價也是很高的:
  
  ●  享元模式使得系統更加複雜。為了使物件可以共享,需要將一些狀態外部化,這使得程式的邏輯複雜化。
  ●  享元模式將享元物件的狀態外部化,而讀取外部狀態使得執行時間稍微變長。

以上內容來自平時所看書籍和網路資源整理測試所得,如有不完善之處,歡迎指正!

相關推薦

Java設計模式----模式

享元模式 一、 概念 二、享元的用途 三、結構和分類 1、單純享元模式  2、複合享元模式 四、享元模式的優缺點 一、概念  Flyweight在拳擊比賽中指最輕量級,即“蠅量級”或“雨量級”,這裡選擇使用“享元模式”的意譯,是因為這

設計模式—— 模式

方便 表示 復雜 優缺點 強制 port n) 使用場景 nfa 模式簡介 運用共享技術有效地支持大量細粒度地對象。 通常情況下,面向對象技術可以增強系統地靈活性及可擴展性,在系統開發過程中,我們會不斷地增加類和對象。當對象數量過多時,將會帶來系統開銷過高、性能下降等

Java設計模式 模式

享元模式介紹 享元模式適用場景 面向物件技術可以很好的解決一些靈活性或可擴充套件性問題,但在很多情況下需要在系統中增加類和物件的個數。當物件數量太多時,將導致物件建立及垃圾回收的代價過高,造成效能下降等問題。享元模式通過共享相同或者相似的細粒度物件解

設計模式 (模式

享元模式 享元模式是池技術的體現。定義如下: 使用共享物件可有效的支援大量的細粒度的物件。 目的是解決大象建立物件導致的記憶體溢位問題以及程式的效率問題。 享元模式中細粒度物件包含兩種狀態: 內部狀態。 內部狀態是物件可共享出的資訊,儲存在享元內部且不會隨環境改變而改變。

設計模式:備忘錄模式

備忘錄模式:在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可將該物件恢復到原先儲存的狀態。 (1):Originator:負責例項化一個備忘錄Memento類,記錄當前時刻它的內部狀態,作為備份。 (2):Memento:備忘錄類,

軟體設計模式學習模式

> 當系統中存在大量相同或相似的物件時,享元模式是一種較好的解決方案,它通過共享技術實現相同或相似的細粒度物件的複用,從而節約記憶體空間。享元模式提供了一個享元池用於儲存已經建立好的享元物件,並通過享元工廠類將享元物件提供給客戶端使用。 ## 模式動機 使用面向物件技術開發時,很多情況下需要在系統

我所理解的設計模式C++實現——模式Flyweight Pattern

概述 想想我們編輯文件用的wps,文件裡文字很多都是重複的,我們不可能為每一個出現的漢字都建立獨立的空間,這樣代價太大,最好的辦法就是共享其中相同的部分,使得需要建立的物件降到最小,這個就是享元模式的核心,即運用共享技術有效地支援大量細粒度的物件。 享元物件能做到共享的關

Java設計模式之結構型模式模式

一、定義: 享元模式,也就是說在一個系統中如果有多個相同的物件,那麼只共享一份就可以了,不必每個都去例項化一個物件。比如說一個文本系統,每個字母定一個物件,那麼大小寫字母一共就是52個,那麼就要定義52個物件。如果有一個1M的文字,那麼字母是何其的多,如果每個字母都定義一個

《大話設計模式Java程式碼示例之備忘錄模式

備忘錄模式(Memonto):在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,這樣以後就可將該物件恢復到原先儲存的狀態。 package memento; /** * 備忘錄模式(Memento) * 遊戲角色 */ public

淺談Java設計模式責任鏈模式Chain of Responsibility

前言: 接下來我們將要談談責任鏈模式,有多個物件,每個物件持有對下一個物件的引用,這樣就會形成一條鏈,請求在這條鏈上傳遞,直到某一物件決定處理該請求。但是發出者並不清楚到底最終那個物件會處理該請求,所

Java設計模式----觀察者模式

觀察者模式 一、定義 二、結構 具體案例 推模型和拉模型 三、Java提供的對觀察者模式的支援 Observer介面 Observable類 一、定義 觀察者模式是物件的行為模式,又叫釋出-訂閱(Publish/Subscribe)模式

設計模式:觀察者模式

優點:   觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體觀察者列表,每一個具體觀察者都符合一個抽象觀察者的介面。被觀察者並不認識任何一個具體觀察者,它只知道它們都有一個共同的介面。 缺點:   ① 如果在被觀察者之間有迴圈依賴的話,被觀察者會觸發它們

設計模式---模板方法模式

strac string package style prim res 不同的 好的 clas 1、簡介   模板方法模式是類的行為模式。準備一個抽象類,將部分邏輯以具體方法以及具體構造函數的形式實現,然後聲明一些抽象方法來迫使子類實現剩余的邏輯。   不同的子類可以以不同

設計模式——職責鏈模式

設計模式 職責鏈模式設計模式(十八)——職責鏈模式一、職責鏈模式簡介1、職責鏈模式簡介職責鏈模式(Chain Of Responsibility)使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合。將可能處理請求的對象連成一條鏈,並沿著這條鏈傳遞請求,直到有一個對象處理請求為止。2、職責連模式

設計模式——橋接模式

不用 java 高層 ext 部分 獨立 lib 類型 ray 1.描述 將橋接部分與他的實現部分分離,是他們都可以獨立的變化。 2.模式的使用 ·抽象(Abstraction):是一個抽象類,該抽象類含有Implementor的聲明,即維護一個Implementor類型

設計模式——模板方法模式

ring next pri cut 限制 pack [] eat 模式 1.描述 定義一個操作中算法的骨架,而將一些步驟延伸到子類當中。模板方法使子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。 2.模式的使用 ·抽象模板(Abstract Template):

深入理解java虛擬機 Java 語法糖背後的真相

它的 blog 需要 原來 ont 影響 else 階段 board 語法糖(Syntactic Sugar),也叫糖衣語法,是英國計算機科學家彼得·約翰·蘭達(Peter J. Landin)發明的一個術語。指的是,在計算機語言中添加某種語法,這些語法糖雖然不會對語言的功

Java開發筆記布林變數論道與或非

在程式語言的設計之初,它們除了可以進行數學計算,還常常用於邏輯推理和條件判斷。為了實現邏輯判斷的功能,Java引入了一種布林型別boolean,用來表示“真”和“假”。該型別的變數只允許兩個取值,即true和false,其中true對應真值,而false對應假值。 如同數值變數擁有加減乘

Java自學筆記

幾個關鍵字(final/static) final:可以用來修飾類,類的成員,以及區域性變數 注意:final修飾的類不能被繼承,但可以繼承其他類,俗稱“太監類”?           final修飾的方法不可

設計模式橋接模式

橋接模式(Bridge),將抽象部分與他的實現部分分離,使他們都可以獨立地變化 類圖的來源 http://img5.imgtn.bdimg.com/it/u=1548947177,2280329040&fm=26&gp=0.jpg public abstract