【學(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所示:
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所示:
享元模式以共享的方式高效地支持大量細(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所示:
在享元模式結(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中,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中,除了增加一個(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所示:
2.復(fù)合享元模式
將一些單純享元對(duì)象使用組合模式加以組合情妖,還可以形成復(fù)合享元對(duì)象睬关,這樣的復(fù)合享元對(duì)象本身不能共享,但是它們可以分解成單純享元對(duì)象毡证,而后者則可以共享电爹。復(fù)合享元模式的結(jié)構(gòu)如圖14-7所示:
通過(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上做掉