溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java的享元模式是什么

發(fā)布時間:2021-06-22 17:41:10 來源:億速云 閱讀:140 作者:chen 欄目:大數(shù)據(jù)

本篇內(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圖

Java的享元模式是什么

代碼實例

我就不用我和李大爺下棋的例子了,以免在他老大(幼?。┑男撵`上留下創(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é)果:

Java的享元模式是什么

可以看出,我們一共派出了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é)果:

Java的享元模式是什么

結(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ù)學習!

向AI問一下細節(jié)

免責聲明:本站發(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)容。

AI