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