設計模式-享元模式(FlyWeight)

来源:https://www.cnblogs.com/bateman6988/archive/2018/04/16/8672821.html
-Advertisement-
Play Games

一、概念 享元模式是對象的結構模式,它以共用的方式高效的支持大量的細粒度對象,減少對象的數量,並達到節約記憶體的目的。 享元對象能夠做到共用的關鍵,主要是區分了內部狀態和外部狀態,內部狀態是對象是在建立時就已確定了,且它不隨環境的改變而有所不同,所以這些內部狀態就可以共用,而外部狀態是會隨著環境的變化 ...


一、概念

  享元模式是對象的結構模式,它以共用的方式高效的支持大量的細粒度對象,減少對象的數量,並達到節約記憶體的目的。

   享元對象能夠做到共用的關鍵,主要是區分了內部狀態和外部狀態,內部狀態是對象是在建立時就已確定了,且它不隨環境的改變而有所不同,所以這些內部狀態就可以共用,而外部狀態是會隨著環境的變化而會改變的,不可以共用。所以外部狀態必須由客戶端保存,當需要時可以傳給享元對象。

二、模式動機

   當一個系統對於同一個對象類型,有大量的對象實例 ,且這些對象實例裡面的狀態大部分都可以外部化,而對一些不可變的相同內部狀態一組實例,就可以用一個對象代替。這樣就可以減少對角的數量,從而達到節約記憶體目的。

三、模式的結構

  

  角色分析:

    FlyWeight:享元介面,通過這個介面Flyweight可以接受並作用於外部狀態。通過這個介面傳入外部的狀態,在享元對象的方法處理過程中可能會使用到這些數據。

    ConcreteFlyWeight:具體的享元對象,這些對象必須是可以共用的,需要封裝Flyweight的內部狀態。

    UnsharedConcreteFlyweight:非共用的享元實例對象,並非所有的Flyweight實現對象都需要共用,非共用的享元實現對象通常是共用的享元實例對象的組合。

    FlyWeightFactory:主要用來創建並管理共用享元對象,並對外提供訪問共用享元對象的介面,它內部往往有一個共用享元對象的實例池,通過這個實例池來實現享元對象的共用。

    Client:享元客戶端,主要的工作是維持一個對Flyweight對象的引用,通過FlyWeightFactory獲取享元對象,並將客戶端存儲的外部狀態作用於享元對象。

 

  代碼樣例如下:

  

package flyweight.sample;

/**
 * 抽像享元角色,所有具體享元角色的超類,通過這個角色享元接收並作用於外部狀態
* @ClassName: FlyWeight 
* @author beteman6988
* @date 2018年3月31日 上午7:50:48 
*
 */
public interface FlyWeight {

    /**
     * 具體業務邏輯
    * @Title: operation 
    * @param @param extrinsicSate  外部狀態 
    * @return void    
    * @throws
     */
    public void operation(String extrinsicSate);

}


/**
 * 享元對象:可以共用的享元對象
* @ClassName: ConcreteFlyWeight 
* @author beteman6988
* @date 2018年3月31日 上午7:56:13 
*
 */
public class ConcreteFlyWeight implements FlyWeight {

    private String intrinsicState ; //不依賴於環境改變而改變的內部狀態
    
    
    /**
     * 構造方法,傳入享元對象的內部狀態
     * @param intrinsicState:內部狀態
     */
    public ConcreteFlyWeight(String intrinsicState) {
        super();
        this.intrinsicState = intrinsicState;
    }

    /**
     * 具體業務邏輯
    * @Title: operation 
    * @param @param extrinsicSate  外部狀態 
    * @return void    
    * @throws
     */
    @Override
    public void operation(String extrinsicSate) {
        //具體業務邏輯
    }

}



import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 不需要共用的複合享元對象
 * 
 * @ClassName: UnsharedConcreteFlyWeight
 * @author beteman6988
 * @date 2018年3月31日 上午8:05:55
 *
 */
public class UnsharedConcreteFlyWeight implements FlyWeight {

    //享元集合 ,主鍵:代表共用享元的主鍵   值:共用享元主鍵代表的共用享元
    private Map<String,FlyWeight> flies = new HashMap<String,FlyWeight>();
    
    /**
     * 往複合享元對象中添加共用享元
    * @Title: add 
    * @param @param intrinsticState:內部狀態(不一定必須是內部狀態,只要是代表該共用享元的主鍵即可)
    * @param @param oneFlyWeight   
    * @return void    
    * @throws
     */
    public void add(String intrinsticState,FlyWeight oneFlyWeight) {
        this.flies.put(intrinsticState,oneFlyWeight);
    }

    /**
     * 作用於所有無享元的具體業務邏輯
    * @Title: operation 
    * @param @param extrinsicSate  外部狀態 
    * @return void    
    * @throws
     */
    @Override
    public void operation(String extrinsicSate) {
        //具體業務邏輯
    }
    
    public String toString() {
        for(Iterator it=flies.entrySet().iterator();it.hasNext();) {
            Map.Entry entry=(Entry) it.next();
            System.out.println(entry.getValue());
        }
        return null;
    }
    
}


import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 提供共用享元對象或複合享元對象的工廠
 * 
 * @ClassName: FlyWeightFactory
 * @author beteman6988
 * @date 2018年3月31日 上午8:24:14
 *
 */
public class FlyWeightFactory {
    // 共用享元的實例池 ,主鍵:代表共用享元的主鍵       值:共用享元主鍵代表的共用享元
    private Map<String, FlyWeight> flies = new HashMap<String, FlyWeight>();

    /**
     * 返回共用享元對象的工廠方法
    * @Title: factory 
    * @param @param intrinsticState :內部狀態
    * @param @return   
    * @return FlyWeight :共用享元實例  
    * @throws
     */
    public FlyWeight factory(String intrinsticState) {
        // 從實例池獲取需要的享元對象
        FlyWeight flyWeight = this.flies.get(intrinsticState);

        if (null == flyWeight) { // 如果獲取不到則創建享元對象
            flyWeight = new ConcreteFlyWeight(intrinsticState);
            this.flies.put(intrinsticState, flyWeight); // 將創建的享元對象添加到實例池
        }
        return flyWeight;
    }

    /**
     * 返回不共用的複合享元實例
    * @Title: factory 
    * @param @param intrinsticStates
    * @param @return   
    * @return FlyWeight    
    * @throws
     */
    public FlyWeight factory(List<String> intrinsticStates) {
        UnsharedConcreteFlyWeight unsharedFlyWeight=new UnsharedConcreteFlyWeight(); //創建不共用的複合享元
        for(String intrinsticState:intrinsticStates) {
            unsharedFlyWeight.add(intrinsticState, this.factory(intrinsticState));
        }
        return unsharedFlyWeight;
    }
    
    
}



import java.util.ArrayList;
import java.util.List;

public class Client {

    public static void main(String[] args) {
        FlyWeightFactory factory =new FlyWeightFactory();
        
        List  list=new ArrayList();
        list.add("a");
        list.add("a");
        
        FlyWeight three=factory.factory(list);
        System.out.println(three);
        
        FlyWeight one=factory.factory("a");
        FlyWeight two=factory.factory("a");
        System.out.println(one);
        System.out.println(two);
                
    }
}
View Code

代碼運行結果如下:

flyweight.sample.ConcreteFlyWeight@15db9742
null
flyweight.sample.ConcreteFlyWeight@15db9742
flyweight.sample.ConcreteFlyWeight@15db9742

通過結果可以看出,三個對象的hashcode相同,說明是同一個對象。

 

四、模式樣例

   通過分析Integer.valueOf(int i)來分析JDK(JDK1,8.0.131)享元設計模式,首先分析Integer這個類是否可以做為一個享元的實現類,如下圖:

  可以看出這個類的內部狀態為final的value,且這個值是通過構造函數傳入,因為它是final int,所以它的值是不隨環境的改變而受到影響的,所以Integer類可以做為享元對象的實現類來使用。

   再來看valueOf(int i)函數:

   可以看出如果傳入的值>=IntegerCache.low且<=IntegerCache.high ,那麼就直接從IntegerCache.cache[i + (-IntegerCache.low)]里直接去取對象,而不是新建對象,從而實現對象的共用。

  再來看一下IntegerCache這個類,如下圖:

     可以看出它是Integer類的一個靜態私有內部類,它有一個static final Integer cache[]的成員,且這成員變數在類載入時通過靜態初始化塊(語句2)將256(從上面的代碼可以看出 是從 -128到127  共256個Integer對象)個Integer對象初始化到了cache[]數組裡面,只要Integer類通過靜態方法public static Integer valueOf(int i) 就可以直接從這個cache[]裡面取到對應int值的Integer對象(前提是int i值的範圍也是-128到127 ,但是這個緩存數組的大小也是可以改變的,通過語句3 ,在JDK的參數裡面設置-Djava.lang.Integer.IntegerCache.high=整數值,就可以改變大小,但是這個值不能小於127,如果小於127會取預設的127 )。

 

五、與其它模式的關係

  享元模式與單例模式:這兩個模式可以組合使用。通常情況下,享元模式中的享元工廠可以實現成為單例模式。

        享元模式與組合模式:這兩個模式也是可以組合使用的。在享元模式中,存在不需要共用的實現,這些不需要共用的享元通常是對共用的享元對象的組合對象。也就是說,享元模式通常會和組合模式組合使用,來實現更複雜的對象層次結構。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 先看段代碼: 思考一下,能給出準確的輸出順序嗎? 下麵我們一個一個的來瞭解 Event Loop 相關的知識點,最後再一步一步分析出本段代碼最後的輸出順序。 JavaScript是單線程 首先我們先瞭解下進程和線程的概念和關係: 進程: 運行的程式就是一個進程,比如你正在運行的瀏覽器,它會有一個進程 ...
  • 一、Date的構造函數 有四種形式的Date構造函數: 二、返回日期對應的毫秒數 1.Date.parse() Date.parse()接收一個日期字元串,返回該日期對應的毫秒數。 2.Date.UTC() Date.UTC()的參數參數分別為年份,基於0的月份(0-11),月中的哪一天(1-31) ...
  • 原始js中的排序不能滿足: arr.sort(sortNumber);arr.sort(function (a, b) { return b.name < a.name;}); 商城列表-積分由高到低由低到高排列: html: 參考:http://www.jb51.net/article/67458 ...
  • js在處理複雜數據的時候,可能會涉及到引用類型的對象或者數組的copy問題,下麵是兩種複製對象或數組的方法: 一、利用jquery自帶的方法,調用簡單方便 ...
  • 今天在做懶載入的時候遇到的問題,在網上搜索找到的答案不是很清晰,就來寫一下,方便以後使用。 直接上圖吧 官方連接:https://cn.vuejs.org/v2/guide/reactivity.html ...
  • 這個模塊還漏了一個稍微複雜點的API,就是app.render,首先看官網的定義: app.render(view, [locals], callback) view為對應的文件名,locals為一個配置對象,callback為解析完成的回調函數。 涉及到的全局屬性有 view:預設為一個內置模塊, ...
  • 對於前端初學者來說,css浮動部分的知識是一塊比較難以理解的部分,下麵我將把我學習過程中的心得分享給大家。 導讀: 1.css塊級元素講解 2.css中浮動是如何產生的 3.出現浮動後,如何清除浮動(本文將涉及到多種清除浮動的方法) 博客正文: 1.css塊級元素講解 常見的塊級元素主要有以下幾種: ...
  • “金三銀四,金九銀十”,都是要收穫的季節。面對各種面試題,各種概念、原理都要去記,挺枯燥的。本文是面向面試題和實際使用談一下Promise。 Promise是什麼? Promise是JS非同步編程中的重要概念,非同步抽象處理對象,是目前比較流行Javascript非同步編程解決方案之一。這句話說的很明白了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...