本篇內(nèi)容主要講解“Java的享元模式是什么”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Java的享元模式是什么”吧!
站在用戶的角度思考問題,與客戶深入溝通,找到鹿城網(wǎng)站設(shè)計(jì)與鹿城網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、申請(qǐng)域名、虛擬主機(jī)、企業(yè)郵箱。業(yè)務(wù)覆蓋鹿城地區(qū)。
前言
試想一下,如果我們要用程序來(lái)設(shè)計(jì)圍棋游戲,黑子181枚,白子180枚,那我們是不是每下一個(gè)子時(shí),都要去new一個(gè)棋子對(duì)象呢?Java是一門面向?qū)ο笳Z(yǔ)言,我們都知道如果在內(nèi)存中不停的new新對(duì)象時(shí),當(dāng)對(duì)象數(shù)量太多時(shí),又回收不及時(shí)時(shí),將導(dǎo)致運(yùn)行代價(jià)過高,帶來(lái)性能下降等問題。那我們?cè)趺慈ソ鉀Q這類問題呢?下面,將為大家講解本篇的重點(diǎn),Java設(shè)計(jì)模式之——享元模式。
什么是享元模式
為了方便理解,我們先來(lái)看一下享元模式的兩種狀態(tài):
內(nèi)部狀態(tài)(Intrinsic State):是存儲(chǔ)在享元對(duì)象內(nèi)部并且不會(huì)隨環(huán)境改變而改變的狀態(tài),因此內(nèi)部狀態(tài)可以共享。
外部狀態(tài)(Extrinsic State):是隨環(huán)境改變而改變的、不可以共享的狀態(tài)。享元對(duì)象的外部狀態(tài)必須由客戶端保存,并在享元對(duì)象被創(chuàng)建之后,在需要使用的時(shí)候再傳入到享元對(duì)象內(nèi)部。一個(gè)外部狀態(tài)與另一個(gè)外部狀態(tài)之間是相互獨(dú)立的。
享元模式將一個(gè)對(duì)象的狀態(tài)分為內(nèi)部狀態(tài)和外部狀態(tài),其中,二者是相互獨(dú)立的,共享相同的內(nèi)部狀態(tài),通過設(shè)置不同的外部狀態(tài)來(lái)改變對(duì)象的特征,讓一個(gè)對(duì)象擁有不同的特征,但內(nèi)部狀態(tài)始終是共享的,不可改變的。也就是,改變外部狀態(tài)不會(huì)引起內(nèi)部狀態(tài)改變。
可以把圍棋想象成享元模式,他們的大小、形狀、顏色是內(nèi)部狀態(tài),棋子的位置是外部狀態(tài),這樣在設(shè)計(jì)時(shí),只需要設(shè)置黑白棋子兩個(gè)對(duì)象,黑棋共享黑色的內(nèi)部狀態(tài),白棋共享白色的內(nèi)部狀態(tài),棋盤上每個(gè)棋子的位置就是他們的外部狀態(tài),圍棋盤361個(gè)交叉點(diǎn)位置,棋子每落一個(gè)位置(外部狀態(tài)),都不會(huì)改變棋子的顏色(內(nèi)部狀態(tài))。這樣是不是好理解一點(diǎn)。
享元模式一般會(huì)結(jié)合工廠模式使用,目的是為了創(chuàng)建一個(gè)享元工廠來(lái)負(fù)責(zé)維護(hù)享元池(Flyweight Pool),享元池里存放的是具有相同內(nèi)部狀態(tài)的享元對(duì)象。在實(shí)際的日常業(yè)務(wù)的千變?nèi)f化中,能夠共享的內(nèi)部狀態(tài)是很少的,所以享元對(duì)象一般都設(shè)計(jì)為較小的對(duì)象,包含的內(nèi)部狀態(tài)也很少,這種對(duì)象也成為細(xì)粒度對(duì)象。
現(xiàn)在我們來(lái)看一下享元模式的英文定義:
Flyweight Pattern: Use sharing to support large numbers of fine-grained objects efficiently.
翻譯過來(lái)就是:運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度對(duì)象的復(fù)用。(Flyweight我不也不懂為什么國(guó)內(nèi)都翻譯成享元,沒找到資料,可能是根據(jù)這個(gè)模式的作用和特性翻譯來(lái)的,如果有知道的朋友煩請(qǐng)文末留言告知一聲,謝謝?。?/p>
再看一下國(guó)內(nèi)對(duì)享元模式的解釋:
享元模式(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)型模式。
簡(jiǎn)而言之:享元模式的目的就是通過共享不變的部分,達(dá)到減少對(duì)象數(shù)量并節(jié)約內(nèi)存的目的。
享元模式的四個(gè)角色
Flyweight(抽象享元類):接口或抽象類,聲明公共方法,這些方法可以向外界提供對(duì)象的內(nèi)部狀態(tài),設(shè)置外部狀態(tài)。
ConcreteFlyweight(具體享元類):實(shí)現(xiàn)了抽象享元類,其實(shí)例稱為享元對(duì)象。必須是可共享的,需要封裝享元對(duì)象的內(nèi)部狀態(tài);。
UnsharedConcreteFlyweight(非共享具體享元類):非共享的享元實(shí)現(xiàn)對(duì)象,并不是所有的享元對(duì)象都可以共享,非共享的享元對(duì)象通常是享元對(duì)象的組合對(duì)象。
FlyweightFactory(享元工廠類):享元工廠,主要用來(lái)創(chuàng)建并管理共享的享元對(duì)象,并對(duì)外提供訪問共享享元的接口。它針對(duì)抽象享元類編程,將各種類型的具體享元對(duì)象存儲(chǔ)在一個(gè)享元池中,享元池一般設(shè)計(jì)為一個(gè)存儲(chǔ)“鍵值對(duì)”的集合(也可以是其他類型的集合),可以結(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ǔ)在享元池中。
享元模式的UML圖
代碼實(shí)例
我就不用我和李大爺下棋的例子了,以免在他老大(幼?。┑男撵`上留下創(chuàng)傷。關(guān)于棋子的案例,網(wǎng)上也有很多版本,大家感興趣的可以自己去看。下面我們用王者榮耀游戲來(lái)舉例。我們知道,在一局對(duì)戰(zhàn)賽里,每隔幾分鐘就會(huì)出現(xiàn)一波小兵和超級(jí)兵,小兵都長(zhǎng)的一模一樣,超級(jí)兵也是,如果王者團(tuán)隊(duì)在設(shè)計(jì)小兵出場(chǎng)的時(shí)候,每出來(lái)一個(gè)小兵,就new一個(gè)小兵對(duì)象,那么在這個(gè)幾百萬(wàn)甚至更多人同時(shí)在線角逐的游戲里,服務(wù)器壓力根本就頂不住,還能不能好好的、流暢的、愉快的上分了,小學(xué)生放學(xué)后早就乖乖在家做作業(yè)了。
那么怎樣設(shè)計(jì)呢?我們可以將小兵的體征、裝配、兵種作為內(nèi)部狀態(tài),然后它們?cè)诘貓D上出擊的方向作為外部狀態(tài),這樣無(wú)論小兵從哪個(gè)方向出擊(外部狀態(tài)怎樣改變),都不會(huì)改變小兵的體征和兵種(內(nèi)部狀態(tài)),這樣我們?cè)陂_發(fā)時(shí),每個(gè)兵種只要有一個(gè)享元對(duì)象就可以了。來(lái)看代碼:
1、編寫抽象享元類
package com.weiya.mazhichu.designpatterns.flyweight; /** ** 功能:抽象享元類 *
* * @author Moore * @ClassName Soldier flyweight. * @Version V1.0. * @date 2019.09.03 21:06:52 */ public interface SoldierFlyweight { /** ** 功能:敵軍出擊方法 *
* * @param direction : * @author Moore * @date 2019.09.03 21:06:52 */ public void attack(String direction); }
2、編寫具體享元類
package com.weiya.mazhichu.designpatterns.flyweight; /** ** 功能:具體享元類 *
* * @author Moore * @ClassName Concrete solider flyweight. * @Version V1.0. * @date 2019.09.04 09:45:41 */ public class ConcreteSoliderFlyweight implements SoldierFlyweight { // 內(nèi)部狀態(tài) private String soliderType; public ConcreteSoliderFlyweight(String soliderType) { this.soliderType = soliderType; } @Override public void attack(String direction) { if("normal".equals(soliderType)){ System.out.println("普通兵加入戰(zhàn)場(chǎng)"); } if("super".equals(soliderType)){ System.out.println("超級(jí)兵加入戰(zhàn)場(chǎng)"); } System.out.println("出擊方向:"+direction); } }
3、編寫享元工廠
package com.weiya.mazhichu.designpatterns.flyweight; import java.util.HashMap; import java.util.Map; /** ** 功能:享元工廠 *
* * @author Moore * @ClassName Soldier fly weight factory. * @Version V1.0. * @date 2019.09.03 21:06:58 */ public class SoldierFlyWeightFactory { //工廠實(shí)例 private static SoldierFlyWeightFactory INSTANCE; // 享元池 private static MapsoldierMap = new HashMap (); private SoldierFlyWeightFactory(){ SoldierFlyweight normalSoldier = new ConcreteSoliderFlyweight("normal"); soldierMap.put("normal",normalSoldier); SoldierFlyweight superSolider = new ConcreteSoliderFlyweight("super"); soldierMap.put("super",superSolider); } /** * * 功能:獲取工廠實(shí)例 *
* * @return soldier fly weight factory * @author Moore * @date 2019.09.03 21:07:02 */ public static SoldierFlyWeightFactory getInstance(){ if(INSTANCE == null){ INSTANCE = new SoldierFlyWeightFactory(); return INSTANCE; } return INSTANCE; } /** ** 功能:獲取享元對(duì)象 *
* * @param soliderType : * @return soldier flyweight * @author Moore * @date 2019.09.03 21:07:02 */ public SoldierFlyweight getSolider(String soliderType){ return soldierMap.get(soliderType); } /** ** 功能:獲取享元池對(duì)象數(shù)量 *
* * @return int * @author Moore * @date 2019.09.03 21:07:02 */ public int getSoliderSize(){ return soldierMap.size(); } }
4、客戶端測(cè)試
package com.weiya.mazhichu.designpatterns.flyweight; /** ** 功能: *
* * @author Moore * @ClassName Honour of kings test. * @Version V1.0. * @date 2019.09.03 21:06:44 */ public class HonourOfKingsTest { public static void main(String[] args) { System.out.println("敵軍還有五秒到達(dá)戰(zhàn)場(chǎng)!"); SoldierFlyWeightFactory factory = SoldierFlyWeightFactory.getInstance(); SoldierFlyweight soldier1 = factory.getSolider("normal"); SoldierFlyweight soldier2 = factory.getSolider("normal"); SoldierFlyweight soldier3 = factory.getSolider("normal"); soldier1.attack("上路"); soldier2.attack("中路"); soldier3.attack("下路"); System.out.println(soldier1 == soldier2); System.out.println(soldier2 == soldier3); System.out.println("--------------------------"); System.out.println("主宰已被擊敗!"); SoldierFlyweight soldier4 = factory.getSolider("super"); SoldierFlyweight soldier5 = factory.getSolider("super"); SoldierFlyweight soldier6 = factory.getSolider("super"); soldier4.attack("上路"); soldier5.attack("中路"); soldier6.attack("下路"); System.out.println("對(duì)方法師殘血,被超級(jí)兵打死..."); System.out.println(soldier4 == soldier5); System.out.println(soldier5 == soldier6); System.out.println("--------------------------"); System.out.println("該案例一共生成對(duì)象:">
查看運(yùn)行結(jié)果:
可以看出,我們一共派出了6個(gè)小兵,其中3個(gè)普通兵,3個(gè)超級(jí)兵,但是享元池中只有兩個(gè)對(duì)象(一個(gè)普通兵、一個(gè)超級(jí)兵對(duì)象),也就是說,無(wú)論派出多少普通兵或者超級(jí)兵,無(wú)論它們要從哪一路出擊,都不會(huì)影響兵的內(nèi)部狀態(tài),從而讓整個(gè)系統(tǒng)的對(duì)象大大減少,減少內(nèi)存消耗,不卡就不影響游戲體驗(yàn),小學(xué)生又可以開心快樂的出來(lái)坑人了,但是要以學(xué)業(yè)為重哦!
享元模式擴(kuò)展
在上面的實(shí)例中,我們主要講的是具體的享元對(duì)象,也就是所有的享元對(duì)象都是必須共享的。但是享元模式的四個(gè)角色中還有一個(gè)非共享的享元實(shí)現(xiàn)對(duì)象,什么意思呢,顧名思義就是享元對(duì)象不一定要共享,但是它通常是作為享元對(duì)象的組合對(duì)象來(lái)使用。從這個(gè)層面來(lái)說,我們又把享元對(duì)象分為:
單純享元模式:在單純享元模式中,所有的享元對(duì)象都是可以共享的,即所有抽象享元類的子類都可共享,不存在非共享具體享元類。
復(fù)合享元模式:將一些單純享元使用組合模式加以組合,可以形成復(fù)合享元對(duì)象,這樣的復(fù)合享元對(duì)象本身不能共享,但是它們可以分解成單純享元對(duì)象,而后者則可以共享。(復(fù)合的享元對(duì)象實(shí)現(xiàn)了抽象享元類,它的實(shí)例就是非共享的享元實(shí)現(xiàn)對(duì)象)
復(fù)合享元模式中,組成復(fù)合享元對(duì)象的每個(gè)單純享元對(duì)象擁有自己的內(nèi)部狀態(tài),而每個(gè)單純享元對(duì)象的外部狀態(tài)都和復(fù)合享元對(duì)象的外部狀態(tài)相同。所以復(fù)合享元模式可以對(duì)多個(gè)單純享元對(duì)象設(shè)置相同的外部狀態(tài), 這也是復(fù)合享元模式的應(yīng)用場(chǎng)景。
單純的享元模式我就不再贅述了,看上面的棋子或者農(nóng)藥的實(shí)例,下面主要說一下組合享元模式,以及它為何非共享,來(lái)看代碼:
1、編寫復(fù)合享元角色類
package com.weiya.mazhichu.designpatterns.flyweight; import java.util.HashMap; import java.util.Map; /** ** 功能: 復(fù)合享元角色類(非共享享元實(shí)現(xiàn)對(duì)象) *
* * @author Moore * @ClassName Concrete composite solider flyweight. * @Version V1.0. * @date 2019.09.04 10:56:11 */ public class ConcreteCompositeSoliderFlyweight implements SoldierFlyweight { private static MapsoldierMap = new HashMap (); /** * * 功能: 增加單純享元對(duì)象 *
* * @param soliderType : * @param flyweight : * @author Moore * @date 2019.09.04 10:56:11 */ public void add(String soliderType,SoldierFlyweight flyweight){ soldierMap.put(soliderType,flyweight); } /** ** 功能: flyWeights是單純享元對(duì)象的集合,它們具有相同的外部狀態(tài)extrinsicState, * 調(diào)用的時(shí)候使用循環(huán)調(diào)用單純享元對(duì)象的attack方法 *
* * @param direction : * @author Moore * @date 2019.09.03 21:06:52 */ @Override public void attack(String direction) { SoldierFlyweight flyweight = null; for(String str : soldierMap.keySet()){ flyweight = soldierMap.get(str); flyweight.attack(direction); } } /** * 移除單純享元對(duì)象. * @param soliderType */ private void remove(String soliderType) { soldierMap.remove(soliderType); } }
2、修改后的享元工廠角色類
package com.weiya.mazhichu.designpatterns.flyweight; import java.util.HashMap; import java.util.List; import java.util.Map; /** ** 功能:享元工廠 *
* * @author Moore * @ClassName Soldier fly weight factory. * @Version V1.0. * @date 2019.09.03 21:06:58 */ public class SoldierFlyWeightFactory { //工廠實(shí)例 private static SoldierFlyWeightFactory INSTANCE; // 享元池 private static MapsoldierMap = new HashMap (); private SoldierFlyWeightFactory(){ SoldierFlyweight normalSoldier = new ConcreteSoliderFlyweight("normal"); soldierMap.put("normal",normalSoldier); SoldierFlyweight superSolider = new ConcreteSoliderFlyweight("super"); soldierMap.put("super",superSolider); } /** * * 功能:獲取工廠實(shí)例 *
* * @return soldier fly weight factory * @author Moore * @date 2019.09.03 21:07:02 */ public static SoldierFlyWeightFactory getInstance(){ if(INSTANCE == null){ INSTANCE = new SoldierFlyWeightFactory(); return INSTANCE; } return INSTANCE; } /** ** 功能:獲取享元對(duì)象(單純享元工廠方法) *
* * @param soliderType : * @return soldier flyweight * @author Moore * @date 2019.09.03 21:07:02 */ public SoldierFlyweight getSolider(String soliderType){ return soldierMap.get(soliderType); } /** ** 功能:復(fù)合享元工廠方法 *
* * @param compositeSoliderTypes : * @return soldier flyweight * @author Moore * @date 2019.09.04 11:06:24 */ public SoldierFlyweight getCompositeSolider(ListcompositeSoliderTypes){ ConcreteCompositeSoliderFlyweight compositeFlyweight = new ConcreteCompositeSoliderFlyweight(); for(String soliderType : compositeSoliderTypes){ compositeFlyweight.add(soliderType,this.getSolider(soliderType)); } return compositeFlyweight; } /** * * 功能:獲取享元池對(duì)象數(shù)量 *
* * @return int * @author Moore * @date 2019.09.03 21:07:02 */ public int getSoliderSize(){ return soldierMap.size(); } }
3、編寫測(cè)試類
package com.weiya.mazhichu.designpatterns.flyweight; import java.util.ArrayList; import java.util.List; /** ** 功能: 測(cè)試單純享元模式和復(fù)合享元模式 *
* * @author Moore * @ClassName Flyweight test. * @Version V1.0. * @date 2019.09.04 11:08:51 */ public class FlyweightTest { public static void main(String[] args) { SoldierFlyWeightFactory factory = SoldierFlyWeightFactory.getInstance(); String soliderType = "normal"; SoldierFlyweight soldierFlyweight1 = factory.getSolider(soliderType); SoldierFlyweight soldierFlyweight2 = factory.getSolider(soliderType); soldierFlyweight1.attack("上路"); soldierFlyweight2.attack("中路"); System.out.println("---------------------------------"); ListcompositeSoliderType = new ArrayList (); compositeSoliderType.add("normal"); compositeSoliderType.add("super"); compositeSoliderType.add("normal"); compositeSoliderType.add("super"); compositeSoliderType.add("normal"); SoldierFlyweight compositeSoliderFlyeweight1 = factory.getSolider(compositeSoliderType); SoldierFlyweight compositeSoliderFlyeweight2 = factory.getSolider(compositeSoliderType); compositeSoliderFlyeweight1.attack("上路"); compositeSoliderFlyeweight2.attack("中路"); System.out.println("---------------------------------"); System.out.println("單純享元模式是否共享對(duì)象:">
查看運(yùn)行結(jié)果:
結(jié)合運(yùn)行結(jié)果,再來(lái)逐字逐句看一下這一段,你應(yīng)該就能有所體會(huì)了。
復(fù)合享元模式中,組成復(fù)合享元對(duì)象的每個(gè)單純享元對(duì)象擁有自己的內(nèi)部狀態(tài),而每個(gè)單純享元對(duì)象的外部狀態(tài)都和復(fù)合享元對(duì)象的外部狀態(tài)相同。所以復(fù)合享元模式可以對(duì)多個(gè)單純享元對(duì)象設(shè)置相同的外部狀態(tài), 這也是復(fù)合享元模式的應(yīng)用場(chǎng)景。
復(fù)合享元模式UML圖
享元模式總結(jié)
使用場(chǎng)景
系統(tǒng)有大量相似或者相同對(duì)象。由于這類對(duì)象的大量使用,造成內(nèi)存的大量耗費(fèi)。
需要緩沖池的場(chǎng)景,(享元池,也就是在需要多次使用享元對(duì)象的時(shí)候)。
對(duì)象的大部分狀態(tài)都可以外部化,可以將這些外部狀態(tài)傳入對(duì)象中。
優(yōu)點(diǎn)
大大減少對(duì)象的創(chuàng)建,降低系統(tǒng)的內(nèi)存,使效率提高。
享元模式的外部狀態(tài)相對(duì)獨(dú)立,而且不會(huì)影響其內(nèi)部狀態(tài),從而使得享元對(duì)象可以在不同的環(huán)境中被共享。
缺點(diǎn)
需要分離出外部狀態(tài)和內(nèi)部狀態(tài),提高了系統(tǒng)的復(fù)雜度。
讀取享元模式的外部狀態(tài)會(huì)使得運(yùn)行時(shí)間稍微變長(zhǎng)。
到此,相信大家對(duì)“Java的享元模式是什么”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!