結(jié)構(gòu)型SEQ6 - 享元模式 Flyweight Pattern

【學(xué)習(xí)難度:★★★★☆擦秽,使用頻率:★☆☆☆☆】
直接出處:享元模式
梳理和學(xué)習(xí):https://github.com/BruceOuyang/boy-design-pattern
簡(jiǎn)書(shū)日期: 2018/03/15
簡(jiǎn)書(shū)首頁(yè):http://www.reibang.com/p/0fb891a7c5ed

實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(一)

當(dāng)前咱們國(guó)家正在大力倡導(dǎo)構(gòu)建和諧社會(huì)岳枷,其中一個(gè)很重要的組成部分就是建設(shè)資源節(jié)約型社會(huì)溃论,“浪費(fèi)可恥弓乙,節(jié)儉光榮”。在軟件系統(tǒng)中煎源,有時(shí)候也會(huì)存在資源浪費(fèi)的情況羹铅,例如在計(jì)算機(jī)內(nèi)存中存儲(chǔ)了多個(gè)完全相同或者非常相似的對(duì)象,如果這些對(duì)象的數(shù)量太多將導(dǎo)致系統(tǒng)運(yùn)行代價(jià)過(guò)高坐漏,內(nèi)存屬于計(jì)算機(jī)的“稀缺資源”宋彼,不應(yīng)該用來(lái)“隨便浪費(fèi)”,那么是否存在一種技術(shù)可以用于節(jié)約內(nèi)存使用空間仙畦,實(shí)現(xiàn)對(duì)這些相同或者相似對(duì)象的共享訪問(wèn)呢?答案是肯定音婶,這種技術(shù)就是我們本章將要學(xué)習(xí)的享元模式慨畸。

14.1 圍棋棋子的設(shè)計(jì)

Sunny軟件公司欲開(kāi)發(fā)一個(gè)圍棋軟件,其界面效果如圖14-1所示:

圖14-1 圍棋軟件界面效果圖

Sunny軟件公司開(kāi)發(fā)人員通過(guò)對(duì)圍棋軟件進(jìn)行分析衣式,發(fā)現(xiàn)在圍棋棋盤(pán)中包含大量的黑子和白子寸士,它們的形狀檐什、大小都一模一樣,只是出現(xiàn)的位置不同而已弱卡。如果將每一個(gè)棋子都作為一個(gè)獨(dú)立的對(duì)象存儲(chǔ)在內(nèi)存中乃正,將導(dǎo)致該圍棋軟件在運(yùn)行時(shí)所需內(nèi)存空間較大,如何降低運(yùn)行代價(jià)婶博、提高系統(tǒng)性能是Sunny公司開(kāi)發(fā)人員需要解決的一個(gè)問(wèn)題瓮具。為了解決這個(gè)問(wèn)題,Sunny公司開(kāi)發(fā)人員決定使用享元模式來(lái)設(shè)計(jì)該圍棋軟件的棋子對(duì)象凡人,那么享元模式是如何實(shí)現(xiàn)節(jié)約內(nèi)存進(jìn)而提高系統(tǒng)性能的呢名党?別著急,下面讓我們正式進(jìn)入享元模式的學(xué)習(xí)挠轴。

14.2 享元模式概述

當(dāng)一個(gè)軟件系統(tǒng)在運(yùn)行時(shí)產(chǎn)生的對(duì)象數(shù)量太多传睹,將導(dǎo)致運(yùn)行代價(jià)過(guò)高,帶來(lái)系統(tǒng)性能下降等問(wèn)題岸晦。例如在一個(gè)文本字符串中存在很多重復(fù)的字符欧啤,如果每一個(gè)字符都用一個(gè)單獨(dú)的對(duì)象來(lái)表示,將會(huì)占用較多的內(nèi)存空間启上,那么我們?nèi)绾稳ケ苊庀到y(tǒng)中出現(xiàn)大量相同或相似的對(duì)象邢隧,同時(shí)又不影響客戶端程序通過(guò)面向?qū)ο蟮姆绞綄?duì)這些對(duì)象進(jìn)行操作?享元模式正為解決這一類(lèi)問(wèn)題而誕生碧绞。享元模式通過(guò)共享技術(shù)實(shí)現(xiàn)相同或相似對(duì)象的重用府框,在邏輯上每一個(gè)出現(xiàn)的字符都有一個(gè)對(duì)象與之對(duì)應(yīng),然而在物理上它們卻共享同一個(gè)享元對(duì)象讥邻,這個(gè)對(duì)象可以出現(xiàn)在一個(gè)字符串的不同地方迫靖,相同的字符對(duì)象都指向同一個(gè)實(shí)例,在享元模式中兴使,存儲(chǔ)這些共享實(shí)例對(duì)象的地方稱為享元池(Flyweight Pool)系宜。我們可以針對(duì)每一個(gè)不同的字符創(chuàng)建一個(gè)享元對(duì)象,將其放在享元池中发魄,需要時(shí)再?gòu)南碓厝〕鲰锬痢H鐖D14-2所示:

圖14-2 字符享元對(duì)象示意圖

享元模式以共享的方式高效地支持大量細(xì)粒度對(duì)象的重用,享元對(duì)象能做到共享的關(guān)鍵是區(qū)分了內(nèi)部狀態(tài)(Intrinsic State)和外部狀態(tài)(Extrinsic State)励幼。下面將對(duì)享元的內(nèi)部狀態(tài)和外部狀態(tài)進(jìn)行簡(jiǎn)單的介紹:

(1) 內(nèi)部狀態(tài)是存儲(chǔ)在享元對(duì)象內(nèi)部并且不會(huì)隨環(huán)境改變而改變的狀態(tài)汰寓,內(nèi)部狀態(tài)可以共享。如字符的內(nèi)容苹粟,不會(huì)隨外部環(huán)境的變化而變化有滑,無(wú)論在任何環(huán)境下字符“a”始終是“a”,都不會(huì)變成“b”嵌削。

(2) 外部狀態(tài)是隨環(huán)境改變而改變的毛好、不可以共享的狀態(tài)望艺。享元對(duì)象的外部狀態(tài)通常由客戶端保存,并在享元對(duì)象被創(chuàng)建之后肌访,需要使用的時(shí)候再傳入到享元對(duì)象內(nèi)部找默。一個(gè)外部狀態(tài)與另一個(gè)外部狀態(tài)之間是相互獨(dú)立的。如字符的顏色吼驶,可以在不同的地方有不同的顏色惩激,例如有的“a”是紅色的,有的“a”是綠色的旨剥,字符的大小也是如此咧欣,有的“a”是五號(hào)字,有的“a”是四號(hào)字轨帜。而且字符的顏色和大小是兩個(gè)獨(dú)立的外部狀態(tài)魄咕,它們可以獨(dú)立變化,相互之間沒(méi)有影響蚌父,客戶端可以在使用時(shí)將外部狀態(tài)注入享元對(duì)象中哮兰。

正因?yàn)閰^(qū)分了內(nèi)部狀態(tài)和外部狀態(tài),我們可以將具有相同內(nèi)部狀態(tài)的對(duì)象存儲(chǔ)在享元池中苟弛,享元池中的對(duì)象是可以實(shí)現(xiàn)共享的喝滞,需要的時(shí)候就將對(duì)象從享元池中取出,實(shí)現(xiàn)對(duì)象的復(fù)用膏秫。通過(guò)向取出的對(duì)象注入不同的外部狀態(tài)右遭,可以得到一系列相似的對(duì)象,而這些對(duì)象在內(nèi)存中實(shí)際上只存儲(chǔ)一份缤削。

享元模式定義如下:

享元模式(Flyweight Pattern):運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度對(duì)象的復(fù)用窘哈。系統(tǒng)只使用少量的對(duì)象,而這些對(duì)象都很相似亭敢,狀態(tài)變化很小滚婉,可以實(shí)現(xiàn)對(duì)象的多次復(fù)用。由于享元模式要求能夠共享的對(duì)象必須是細(xì)粒度對(duì)象帅刀,因此它又稱為輕量級(jí)模式让腹,它是一種對(duì)象結(jié)構(gòu)型模式。

實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(二)

享元模式結(jié)構(gòu)較為復(fù)雜扣溺,一般結(jié)合工廠模式一起使用骇窍,在它的結(jié)構(gòu)圖中包含了一個(gè)享元工廠類(lèi),其結(jié)構(gòu)圖如圖14-3所示:

圖14-3 享元模式結(jié)構(gòu)圖

在享元模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:

  • Flyweight(抽象享元類(lèi)):通常是一個(gè)接口或抽象類(lèi)锥余,在抽象享元類(lèi)中聲明了具體享元類(lèi)公共的方法像鸡,這些方法可以向外界提供享元對(duì)象的內(nèi)部數(shù)據(jù)(內(nèi)部狀態(tài)),同時(shí)也可以通過(guò)這些方法來(lái)設(shè)置外部數(shù)據(jù)(外部狀態(tài))。

  • ConcreteFlyweight(具體享元類(lèi)):它實(shí)現(xiàn)了抽象享元類(lèi)只估,其實(shí)例稱為享元對(duì)象;在具體享元類(lèi)中為內(nèi)部狀態(tài)提供了存儲(chǔ)空間着绷。通常我們可以結(jié)合單例模式來(lái)設(shè)計(jì)具體享元類(lèi)蛔钙,為每一個(gè)具體享元類(lèi)提供唯一的享元對(duì)象。

  • UnsharedConcreteFlyweight(非共享具體享元類(lèi)):并不是所有的抽象享元類(lèi)的子類(lèi)都需要被共享荠医,不能被共享的子類(lèi)可設(shè)計(jì)為非共享具體享元類(lèi)吁脱;當(dāng)需要一個(gè)非共享具體享元類(lèi)的對(duì)象時(shí)可以直接通過(guò)實(shí)例化創(chuàng)建。

  • FlyweightFactory(享元工廠類(lèi)):享元工廠類(lèi)用于創(chuàng)建并管理享元對(duì)象彬向,它針對(duì)抽象享元類(lèi)編程兼贡,將各種類(lèi)型的具體享元對(duì)象存儲(chǔ)在一個(gè)享元池中,享元池一般設(shè)計(jì)為一個(gè)存儲(chǔ)“鍵值對(duì)”的集合(也可以是其他類(lèi)型的集合)娃胆,可以結(jié)合工廠模式進(jìn)行設(shè)計(jì)遍希;當(dāng)用戶請(qǐng)求一個(gè)具體享元對(duì)象時(shí),享元工廠提供一個(gè)存儲(chǔ)在享元池中已創(chuàng)建的實(shí)例或者創(chuàng)建一個(gè)新的實(shí)例(如果不存在的話)里烦,返回新創(chuàng)建的實(shí)例并將其存儲(chǔ)在享元池中凿蒜。

在享元模式中引入了享元工廠類(lèi),享元工廠類(lèi)的作用在于提供一個(gè)用于存儲(chǔ)享元對(duì)象的享元池胁黑,當(dāng)用戶需要對(duì)象時(shí)废封,首先從享元池中獲取,如果享元池中不存在丧蘸,則創(chuàng)建一個(gè)新的享元對(duì)象返回給用戶漂洋,并在享元池中保存該新增對(duì)象。典型的享元工廠類(lèi)的代碼如下:

class FlyweightFactory {
    //定義一個(gè)HashMap用于存儲(chǔ)享元對(duì)象力喷,實(shí)現(xiàn)享元池
    private HashMap flyweights = newHashMap();

    public Flyweight getFlyweight(String key){
           //如果對(duì)象存在刽漂,則直接從享元池獲取
           if(flyweights.containsKey(key)){
                  return(Flyweight)flyweights.get(key);
           }
           //如果對(duì)象不存在,先創(chuàng)建一個(gè)新的對(duì)象添加到享元池中冗懦,然后返回
           else {
                  Flyweight fw = newConcreteFlyweight();
                  flyweights.put(key,fw);
                  return fw;
           }
    }
}

享元類(lèi)的設(shè)計(jì)是享元模式的要點(diǎn)之一爽冕,在享元類(lèi)中要將內(nèi)部狀態(tài)和外部狀態(tài)分開(kāi)處理,通常將內(nèi)部狀態(tài)作為享元類(lèi)的成員變量披蕉,而外部狀態(tài)通過(guò)注入的方式添加到享元類(lèi)中颈畸。典型的享元類(lèi)代碼如下所示:

class Flyweight {
    //內(nèi)部狀態(tài)intrinsicState作為成員變量,同一個(gè)享元對(duì)象其內(nèi)部狀態(tài)是一致的
    private String intrinsicState;

    public  Flyweight(String intrinsicState) {
           this.intrinsicState=intrinsicState;
    }

     //外部狀態(tài)extrinsicState在使用時(shí)由外部設(shè)置没讲,不保存在享元對(duì)象中眯娱,即使是同一個(gè)對(duì)象,在每一次調(diào)用時(shí)也可以傳入不同的外部狀態(tài)
    public void operation(String  extrinsicState) {
           ......
    }     
}

實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(三)

14.3 完整解決方案

為了節(jié)約存儲(chǔ)空間爬凑,提高系統(tǒng)性能徙缴,Sunny公司開(kāi)發(fā)人員使用享元模式來(lái)設(shè)計(jì)圍棋軟件中的棋子,其基本結(jié)構(gòu)如圖14-4所示:

圖14-4 圍棋棋子結(jié)構(gòu)圖

在圖14-4中,IgoChessman充當(dāng)抽象享元類(lèi)于样,BlackIgoChessman和WhiteIgoChessman充當(dāng)具體享元類(lèi)疏叨,IgoChessmanFactory充當(dāng)享元工廠類(lèi)。完整代碼如下所示:

import java.util.*;  

//圍棋棋子類(lèi):抽象享元類(lèi)  
abstract class IgoChessman {  
    public abstract String getColor();  

    public void display() {  
        System.out.println("棋子顏色:" + this.getColor());    
    }  
}  

//黑色棋子類(lèi):具體享元類(lèi)  
class BlackIgoChessman extends IgoChessman {  
    public String getColor() {  
        return "黑色";  
    }     
}  

//白色棋子類(lèi):具體享元類(lèi)  
class WhiteIgoChessman extends IgoChessman {  
    public String getColor() {  
        return "白色";  
    }  
}  

//圍棋棋子工廠類(lèi):享元工廠類(lèi)穿剖,使用單例模式進(jìn)行設(shè)計(jì)  
class IgoChessmanFactory {  
    private static IgoChessmanFactory instance = new IgoChessmanFactory();  
    private static Hashtable ht; //使用Hashtable來(lái)存儲(chǔ)享元對(duì)象蚤蔓,充當(dāng)享元池  

    private IgoChessmanFactory() {  
        ht = new Hashtable();  
        IgoChessman black,white;  
        black = new BlackIgoChessman();  
        ht.put("b",black);  
        white = new WhiteIgoChessman();  
        ht.put("w",white);  
    }  

    //返回享元工廠類(lèi)的唯一實(shí)例  
    public static IgoChessmanFactory getInstance() {  
        return instance;  
    }  

    //通過(guò)key來(lái)獲取存儲(chǔ)在Hashtable中的享元對(duì)象  
    public static IgoChessman getIgoChessman(String color) {  
        return (IgoChessman)ht.get(color);    
    }  
}

編寫(xiě)如下客戶端測(cè)試代碼:

class Client {  
    public static void main(String args[]) {  
        IgoChessman black1,black2,black3,white1,white2;  
        IgoChessmanFactory factory;  

        //獲取享元工廠對(duì)象  
        factory = IgoChessmanFactory.getInstance();  

        //通過(guò)享元工廠獲取三顆黑子  
        black1 = factory.getIgoChessman("b");  
        black2 = factory.getIgoChessman("b");  
        black3 = factory.getIgoChessman("b");  
        System.out.println("判斷兩顆黑子是否相同:" + (black1==black2));  

        //通過(guò)享元工廠獲取兩顆白子  
        white1 = factory.getIgoChessman("w");  
        white2 = factory.getIgoChessman("w");  
        System.out.println("判斷兩顆白子是否相同:" + (white1==white2));  

        //顯示棋子  
        black1.display();  
        black2.display();  
        black3.display();  
        white1.display();  
        white2.display();  
    }  
}

編譯并運(yùn)行程序,輸出結(jié)果如下:

判斷兩顆黑子是否相同:true
判斷兩顆白子是否相同:true
棋子顏色:黑色
棋子顏色:黑色
棋子顏色:黑色
棋子顏色:白色
棋子顏色:白色

從輸出結(jié)果可以看出糊余,雖然我們獲取了三個(gè)黑子對(duì)象和兩個(gè)白子對(duì)象秀又,但是它們的內(nèi)存地址相同,也就是說(shuō)贬芥,它們實(shí)際上是同一個(gè)對(duì)象吐辙。在實(shí)現(xiàn)享元工廠類(lèi)時(shí)我們使用了單例模式和簡(jiǎn)單工廠模式,確保了享元工廠對(duì)象的唯一性蘸劈,并提供工廠方法來(lái)向客戶端返回享元對(duì)象昏苏。

實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(四)

14.5 帶外部狀態(tài)的解決方案

Sunny軟件公司開(kāi)發(fā)人員通過(guò)對(duì)圍棋棋子進(jìn)行進(jìn)一步分析,發(fā)現(xiàn)雖然黑色棋子和白色棋子可以共享昵时,但是它們將顯示在棋盤(pán)的不同位置捷雕,如何讓相同的黑子或者白子能夠多次重復(fù)顯示且位于一個(gè)棋盤(pán)的不同地方?解決方法就是將棋子的位置定義為棋子的一個(gè)外部狀態(tài)壹甥,在需要時(shí)再進(jìn)行設(shè)置救巷。因此,我們?cè)趫D14-4中增加了一個(gè)新的類(lèi)Coordinates(坐標(biāo)類(lèi))句柠,用于存儲(chǔ)每一個(gè)棋子的位置浦译,修改之后的結(jié)構(gòu)圖如圖14-5所示:

圖14-5 引入外部狀態(tài)之后的圍棋棋子結(jié)構(gòu)圖

在圖14-5中,除了增加一個(gè)坐標(biāo)類(lèi)Coordinates以外溯职,抽象享元類(lèi)IgoChessman中的display()方法也將對(duì)應(yīng)增加一個(gè)Coordinates類(lèi)型的參數(shù)精盅,用于在顯示棋子時(shí)指定其坐標(biāo),Coordinates類(lèi)和修改之后的IgoChessman類(lèi)的代碼如下所示:

//坐標(biāo)類(lèi):外部狀態(tài)類(lèi)  
class Coordinates {  
    private int x;  
    private int y;  

    public Coordinates(int x,int y) {  
        this.x = x;  
        this.y = y;  
    }  

    public int getX() {  
        return this.x;  
    }  

    public void setX(int x) {  
        this.x = x;  
    }  

    public int getY() {  
        return this.y;  
    }  

    public void setY(int y) {  
        this.y = y;  
    }  
}   

//圍棋棋子類(lèi):抽象享元類(lèi)  
abstract class IgoChessman {  
    public abstract String getColor();  

    public void display(Coordinates coord){  
        System.out.println("棋子顏色:" + this.getColor() + "谜酒,棋子位置:" + coord.getX() + "叹俏," + coord.getY() );    
    }  
}

客戶端測(cè)試代碼修改如下:

class Client {  
    public static void main(String args[]) {  
        IgoChessman black1,black2,black3,white1,white2;  
        IgoChessmanFactory factory;  

        //獲取享元工廠對(duì)象  
        factory = IgoChessmanFactory.getInstance();  

        //通過(guò)享元工廠獲取三顆黑子  
        black1 = factory.getIgoChessman("b");  
        black2 = factory.getIgoChessman("b");  
        black3 = factory.getIgoChessman("b");  
        System.out.println("判斷兩顆黑子是否相同:" + (black1==black2));  

        //通過(guò)享元工廠獲取兩顆白子  
        white1 = factory.getIgoChessman("w");  
        white2 = factory.getIgoChessman("w");  
        System.out.println("判斷兩顆白子是否相同:" + (white1==white2));  

        //顯示棋子,同時(shí)設(shè)置棋子的坐標(biāo)位置  
        black1.display(new Coordinates(1,2));  
        black2.display(new Coordinates(3,4));  
        black3.display(new Coordinates(1,3));  
        white1.display(new Coordinates(2,5));  
        white2.display(new Coordinates(2,4));  
    }  
}

編譯并運(yùn)行程序僻族,輸出結(jié)果如下:

判斷兩顆黑子是否相同:true
判斷兩顆白子是否相同:true
棋子顏色:黑色粘驰,棋子位置:1,2
棋子顏色:黑色述么,棋子位置:3蝌数,4
棋子顏色:黑色,棋子位置:1度秘,3
棋子顏色:白色顶伞,棋子位置:2,5
棋子顏色:白色,棋子位置:2唆貌,4

從輸出結(jié)果可以看到滑潘,在每次調(diào)用display()方法時(shí),都設(shè)置了不同的外部狀態(tài)——坐標(biāo)值锨咙,因此相同的棋子對(duì)象雖然具有相同的顏色众羡,但是它們的坐標(biāo)值不同,將顯示在棋盤(pán)的不同位置蓖租。

實(shí)現(xiàn)對(duì)象的復(fù)用——享元模式(五)

14.5 單純享元模式和復(fù)合享元模式

標(biāo)準(zhǔn)的享元模式結(jié)構(gòu)圖中既包含可以共享的具體享元類(lèi),也包含不可以共享的非共享具體享元類(lèi)羊壹。但是在實(shí)際使用過(guò)程中蓖宦,我們有時(shí)候會(huì)用到兩種特殊的享元模式:?jiǎn)渭兿碓J胶蛷?fù)合享元模式,下面將對(duì)這兩種特殊的享元模式進(jìn)行簡(jiǎn)單的介紹:

1.單純享元模式

在單純享元模式中油猫,所有的具體享元類(lèi)都是可以共享的稠茂,不存在非共享具體享元類(lèi)。單純享元模式的結(jié)構(gòu)如圖14-6所示:

圖14-6 單純享元模式結(jié)構(gòu)圖

2.復(fù)合享元模式

將一些單純享元對(duì)象使用組合模式加以組合情妖,還可以形成復(fù)合享元對(duì)象睬关,這樣的復(fù)合享元對(duì)象本身不能共享,但是它們可以分解成單純享元對(duì)象毡证,而后者則可以共享电爹。復(fù)合享元模式的結(jié)構(gòu)如圖14-7所示:

圖14-7 復(fù)合享元模式結(jié)構(gòu)圖

通過(guò)復(fù)合享元模式,可以確保復(fù)合享元類(lèi)CompositeConcreteFlyweight中所包含的每個(gè)單純享元類(lèi)ConcreteFlyweight都具有相同的外部狀態(tài)料睛,而這些單純享元的內(nèi)部狀態(tài)往往可以不同丐箩。如果希望為多個(gè)內(nèi)部狀態(tài)不同的享元對(duì)象設(shè)置相同的外部狀態(tài),可以考慮使用復(fù)合享元模式恤煞。

14.6 關(guān)于享元模式的幾點(diǎn)補(bǔ)充

1.與其他模式的聯(lián)用

享元模式通常需要和其他模式一起聯(lián)用屎勘,幾種常見(jiàn)的聯(lián)用方式如下:

(1)在享元模式的享元工廠類(lèi)中通常提供一個(gè)靜態(tài)的工廠方法用于返回享元對(duì)象,使用簡(jiǎn)單工廠模式來(lái)生成享元對(duì)象居扒。

(2)在一個(gè)系統(tǒng)中概漱,通常只有唯一一個(gè)享元工廠,因此可以使用單例模式進(jìn)行享元工廠類(lèi)的設(shè)計(jì)喜喂。

(3)享元模式可以結(jié)合組合模式形成復(fù)合享元模式瓤摧,統(tǒng)一對(duì)多個(gè)享元對(duì)象設(shè)置外部狀態(tài)。

2.享元模式與String類(lèi)

JDK類(lèi)庫(kù)中的String類(lèi)使用了享元模式夜惭,我們通過(guò)如下代碼來(lái)加以說(shuō)明:

class Demo {
       public  static void main(String args[]) {
              String  str1 = "abcd";
              String  str2 = "abcd";
              String  str3 = "ab" + "cd";
              String  str4 = "ab";
              str4  += "cd";

              System.out.println(str1  == str2);
              System.out.println(str1  == str3);
              System.out.println(str1  == str4);

              str2  += "e";
              System.out.println(str1  == str2);
       }
}

在Java語(yǔ)言中姻灶,如果每次執(zhí)行類(lèi)似String str1="abcd"的操作時(shí)都創(chuàng)建一個(gè)新的字符串對(duì)象將導(dǎo)致內(nèi)存開(kāi)銷(xiāo)很大,因此如果第一次創(chuàng)建了內(nèi)容為"abcd"的字符串對(duì)象str1诈茧,下一次再創(chuàng)建內(nèi)容相同的字符串對(duì)象str2時(shí)會(huì)將它的引用指向"abcd"产喉,不會(huì)重新分配內(nèi)存空間,從而實(shí)現(xiàn)了"abcd"在內(nèi)存中的共享。上述代碼輸出結(jié)果如下:

true
true
false
false

可以看出曾沈,前兩個(gè)輸出語(yǔ)句均為true这嚣,說(shuō)明str1、str2塞俱、str3在內(nèi)存中引用了相同的對(duì)象姐帚;如果有一個(gè)字符串str4,其初值為"ab"障涯,再對(duì)它進(jìn)行操作str4 += "cd"罐旗,此時(shí)雖然str4的內(nèi)容與str1相同,但是由于str4的初始值不同唯蝶,在創(chuàng)建str4時(shí)重新分配了內(nèi)存九秀,所以第三個(gè)輸出語(yǔ)句結(jié)果為false;最后一個(gè)輸出語(yǔ)句結(jié)果也為false粘我,說(shuō)明當(dāng)對(duì)str2進(jìn)行修改時(shí)將創(chuàng)建一個(gè)新的對(duì)象鼓蜒,修改工作在新對(duì)象上完成,而原來(lái)引用的對(duì)象并沒(méi)有發(fā)生任何改變征字,str1仍然引用原有對(duì)象都弹,而str2引用新對(duì)象,str1與str2引用了兩個(gè)完全不同的對(duì)象匙姜。

擴(kuò)展

關(guān)于Java String類(lèi)這種在修改享元對(duì)象時(shí)畅厢,先將原有對(duì)象復(fù)制一份,然后在新對(duì)象上再實(shí)施修改操作的機(jī)制稱為“Copy On Write”搁料,大家可以自行查詢相關(guān)資料來(lái)進(jìn)一步了解和學(xué)習(xí)“Copy On Write”機(jī)制或详,在此不作詳細(xì)說(shuō)明。

14.7 享元模式總結(jié)

當(dāng)系統(tǒng)中存在大量相同或者相似的對(duì)象時(shí)郭计,享元模式是一種較好的解決方案霸琴,它通過(guò)共享技術(shù)實(shí)現(xiàn)相同或相似的細(xì)粒度對(duì)象的復(fù)用,從而節(jié)約了內(nèi)存空間昭伸,提高了系統(tǒng)性能梧乘。相比其他結(jié)構(gòu)型設(shè)計(jì)模式,享元模式的使用頻率并不算太高庐杨,但是作為一種以“節(jié)約內(nèi)存选调,提高性能”為出發(fā)點(diǎn)的設(shè)計(jì)模式,它在軟件開(kāi)發(fā)中還是得到了一定程度的應(yīng)用灵份。

1.主要優(yōu)點(diǎn)

享元模式的主要優(yōu)點(diǎn)如下:

(1) 可以極大減少內(nèi)存中對(duì)象的數(shù)量仁堪,使得相同或相似對(duì)象在內(nèi)存中只保存一份,從而可以節(jié)約系統(tǒng)資源填渠,提高系統(tǒng)性能弦聂。

(2) 享元模式的外部狀態(tài)相對(duì)獨(dú)立鸟辅,而且不會(huì)影響其內(nèi)部狀態(tài),從而使得享元對(duì)象可以在不同的環(huán)境中被共享莺葫。

2.主要缺點(diǎn)

享元模式的主要缺點(diǎn)如下:

(1) 享元模式使得系統(tǒng)變得復(fù)雜匪凉,需要分離出內(nèi)部狀態(tài)和外部狀態(tài),這使得程序的邏輯復(fù)雜化捺檬。

(2) 為了使對(duì)象可以共享再层,享元模式需要將享元對(duì)象的部分狀態(tài)外部化,而讀取外部狀態(tài)將使得運(yùn)行時(shí)間變長(zhǎng)堡纬。

3.適用場(chǎng)景

在以下情況下可以考慮使用享元模式:

(1) 一個(gè)系統(tǒng)有大量相同或者相似的對(duì)象聂受,造成內(nèi)存的大量耗費(fèi)。

(2) 對(duì)象的大部分狀態(tài)都可以外部化烤镐,可以將這些外部狀態(tài)傳入對(duì)象中饺饭。

(3) 在使用享元模式時(shí)需要維護(hù)一個(gè)存儲(chǔ)享元對(duì)象的享元池,而這需要耗費(fèi)一定的系統(tǒng)資源职车,因此,應(yīng)當(dāng)在需要多次重復(fù)使用享元對(duì)象時(shí)才值得使用享元模式鹊杖。

練習(xí)

Sunny軟件公司欲開(kāi)發(fā)一個(gè)多功能文檔編輯器悴灵,在文本文檔中可以插入圖片、動(dòng)畫(huà)骂蓖、視頻等多媒體資料积瞒,為了節(jié)約系統(tǒng)資源,相同的圖片登下、動(dòng)畫(huà)和視頻在同一個(gè)文檔中只需保存一份茫孔,但是可以多次重復(fù)出現(xiàn),而且它們每次出現(xiàn)時(shí)位置和大小均可不同被芳。試使用享元模式設(shè)計(jì)該文檔編輯器缰贝。

練習(xí)會(huì)在我的github上做掉

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市畔濒,隨后出現(xiàn)的幾起案子剩晴,更是在濱河造成了極大的恐慌,老刑警劉巖侵状,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赞弥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡趣兄,警方通過(guò)查閱死者的電腦和手機(jī)绽左,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)艇潭,“玉大人拼窥,你說(shuō)我怎么就攤上這事戏蔑。” “怎么了闯团?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵辛臊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我房交,道長(zhǎng)彻舰,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任候味,我火速辦了婚禮刃唤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘白群。我一直安慰自己尚胞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布帜慢。 她就那樣靜靜地躺著笼裳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粱玲。 梳的紋絲不亂的頭發(fā)上躬柬,一...
    開(kāi)封第一講書(shū)人閱讀 52,337評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音抽减,去河邊找鬼允青。 笑死,一個(gè)胖子當(dāng)著我的面吹牛卵沉,可吹牛的內(nèi)容都是我干的颠锉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼史汗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼琼掠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起停撞,我...
    開(kāi)封第一講書(shū)人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤眉枕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后怜森,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體速挑,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年副硅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姥宝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恐疲,死狀恐怖腊满,靈堂內(nèi)的尸體忽然破棺而出套么,到底是詐尸還是另有隱情,我是刑警寧澤碳蛋,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布胚泌,位于F島的核電站,受9級(jí)特大地震影響肃弟,放射性物質(zhì)發(fā)生泄漏玷室。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一笤受、第九天 我趴在偏房一處隱蔽的房頂上張望穷缤。 院中可真熱鬧,春花似錦箩兽、人聲如沸津肛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)身坐。三九已至,卻和暖如春落包,著一層夾襖步出監(jiān)牢的瞬間掀亥,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工妥色, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人遏片。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓嘹害,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親吮便。 傳聞我的和親對(duì)象是個(gè)殘疾皇子笔呀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容