【設計模式】享元模式(輕量級模式) 單純享元模式和復合享元模式

享元模式(Flyweight Pattern)詳解


一、享元模式簡介

享元模式(Flyweight Pattern) 是一種 結構型設計模式(對象結構型模式),它通過共享技術實現相同或相似對象的重用,以減少內存占用和提高性能。具體來說,就是將一些細粒度的對象集中起來,使得這些對象可以互相共享。

又稱為輕量級模式,要求能夠被共享的對象必須是細粒度對象

運用共享技術有效地支持大量細粒度對象的復用。

簡單來說:

“如果你有一堆相似的對象,為什么不讓他們共享數據,從而節省內存呢?”
字符享元對象示意圖
在這里插入圖片描述

享元模式:通過共享技術實現相同或相似對象的重用
享元池(Flyweight Pool):存儲共享實例對象的地方

原理

  1. 將具有相同內部狀態的對象存儲在享元池中,享元池中的對象是可以實現共享的
  2. 需要的時候將對象從享元池中取出,即可實現對象的復用
  3. 通過向取出的對象注入不同的外部狀態,可以得到一系列相似的對象,而這些對象在內存中實際上只存儲一份

享元模式包含以下4個角色
Flyweight(抽象享元類)
ConcreteFlyweight(具體享元類)
UnsharedConcreteFlyweight(非共享具體享元類)
FlyweightFactory(享元工廠類)

在這里插入圖片描述


二、解決的問題類型

享元模式主要用于解決以下問題:

  • 大量小對象導致內存消耗過大:當你的應用程序需要創建大量相似的小對象時,直接創建這些對象會導致內存使用過高。
  • 提升性能:通過共享相同的對象實例來減少對象創建的數量,進而減少垃圾回收的壓力,提高系統性能。
  • 對象的大部分狀態都可以外部化,可以將這些外部狀態傳入對象中
  • 在使用享元模式時需要維護一個存儲享元對象的享元池,而這需要耗費一定的系統資源,因此,在需要多次重復使用享元對象時才值得使用享元模式

三、使用場景

場景示例
字符串池Java 中的 String.intern() 方法
緩存管理如緩存數據庫查詢結果
文檔處理文本編輯器中的字符樣式管理
游戲開發游戲中大量的相同紋理、模型等資源

四、核心概念

1. 內部狀態 vs 外部狀態

  • 內部狀態:存儲在享元對象內部,可以在多個對象之間共享的部分,且不會隨環境改變而改變的狀態,通常是不變的數據。(例如:字符的內容)
  • 外部狀態:不能被共享的部分,是隨環境變化而變化的數據。享元對象的外部狀態通常由客戶端保存,并在享元對象被創建之后,需要使用的時候再傳入到享元對象內部。一個外部狀態與另一個外部狀態之間是相互獨立的(例如:字符的顏色和大小)

享元模式的核心思想就是分離對象的內部狀態和外部狀態,讓內部狀態可以共享,而外部狀態則由客戶端負責維護。


五、代碼案例(Java)

我們以一個簡單的文本編輯器為例,展示如何利用享元模式來優化字體樣式的管理。

1. 定義享元接口

interface CharacterStyle {void display(char character);
}

2. 創建具體的享元類

class CharacterStyleImpl implements CharacterStyle {private final String fontName;private final int fontSize;public CharacterStyleImpl(String fontName, int fontSize) {this.fontName = fontName;this.fontSize = fontSize;}@Overridepublic void display(char character) {System.out.println(character + " in " + fontName + ", size: " + fontSize);}
}

3. 創建享元工廠

class CharacterStyleFactory {private static final Map<String, CharacterStyle> styles = new HashMap<>();public static CharacterStyle getStyle(String fontName, int fontSize) {String key = fontName + "-" + fontSize;if (!styles.containsKey(key)) {styles.put(key, new CharacterStyleImpl(fontName, fontSize));}return styles.get(key);}
}

4. 使用享元模式

public class Client {public static void main(String[] args) {// 獲取樣式CharacterStyle style1 = CharacterStyleFactory.getStyle("Arial", 12);CharacterStyle style2 = CharacterStyleFactory.getStyle("Arial", 12);// 顯示字符style1.display('A');  // 輸出: A in Arial, size: 12style2.display('B');  // 輸出: B in Arial, size: 12// 驗證是否為同一實例System.out.println(style1 == style_Statics);  // 輸出: true}
}

典型代碼(c++)

典型的抽象享元類代碼

abstract class Flyweight
{public abstract void Operation(string extrinsicState);
}

典型的具體享元類代碼

class ConcreteFlyweight : Flyweight
{//內部狀態intrinsicState作為成員變量,同一個享元對象其內部狀態是一致的private string intrinsicState;public ConcreteFlyweight(string intrinsicState) {this.intrinsicState = intrinsicState;}//外部狀態extrinsicState在使用時由外部設置,不保存在享元對象中,即使是同一個對象,在每一次調用時可以傳入不同的外部狀態public override void Operation(string extrinsicState) {//實現業務方法}	
}

典型的非共享具體享元類代碼

class UnsharedConcreteFlyweight : Flyweight
{public override void Operation(string extrinsicState){//實現業務方法}
}

典型的享元工廠類代碼

using System.Collections;
class FlyweightFactory
{//定義一個Hashtable用于存儲享元對象,實現享元池private Hashtable flyweights = new Hashtable();public Flyweight GetFlyweight(string key){//如果對象存在,則直接從享元池獲取if (flyweights.ContainsKey(key)){return (Flyweight)flyweights[key];}//如果對象不存在,先創建一個新的對象添加到享元池中,然后返回else{Flyweight fw = new ConcreteFlyweight("state");flyweights.Add(key,fw);return fw;}}
}

其他案例

  1. 某軟件公司要開發一個圍棋軟件,其界面效果如下圖所示
    在這里插入圖片描述
    該軟件公司開發人員通過對圍棋軟件進行分析發現,在圖中,圍棋棋盤中包含大量的黑子和白子,它們的形狀、大小都一模一樣,只是出現的位置不同而已。如果將每一個棋子都作為一個獨立的對象存儲在內存中,將導致該圍棋軟件在運行時所需內存空間較大,如何降低運行代價、提高系統性能是需要解決的一個問題。為了解決該問題,現使用享元模式來設計該圍棋軟件的棋子對象。

在這里插入圖片描述
如何讓相同的黑子或者白子能夠多次重復顯示但位于一個棋盤的不同地方?

解決方案:將棋子的位置定義為棋子的一個外部狀態,在需要時再進行設置

引入外部狀態之后的圍棋棋子結構圖
在這里插入圖片描述

//Coordinates.cs
namespace FlyweightSample
{class Coordinates{private int x;private int y;public Coordinates(int x, int y){this.x = x;this.y = y;}public int X{get { return x; }set { x = value; }}   public int Y{get { return y; }set { y = value; }}}
}
//IgoChessman.cs
using System;
namespace FlyweightSample
{abstract class IgoChessman{public abstract string GetColor();public void Display(Coordinates coord){Console.WriteLine("棋子顏色:{0},棋子位置:{1},{2}", this.GetColor(),coord.X,coord.Y);	}}
}
  1. 共享網絡設備(無外部狀態)
    很多網絡設備都是支持共享的,如交換機、集線器等,多臺終端計算機可以連接同一臺網絡設備,并通過該網絡設備進行數據轉發,如圖所示,現用享元模式模擬共享網絡設備的設計原理
    在這里插入圖片描述
  2. 共享網絡設備(有外部狀態)
    雖然網絡設備可以共享,但是分配給每一個終端計算機的端口(Port)是不同的,因此多臺計算機雖然可以共享同一個網絡設備,但必須使用不同的端口。我們可以將端口從網絡設備中抽取出來作為外部狀態,需要時再進行設置
    在這里插入圖片描述

六、優缺點分析

優點描述
? 節省內存減少了大量相似對象的創建,降低了內存使用
? 提高性能減少了對象創建的時間開銷
? 支持大規模并發在多線程環境中也能很好地工作
其他外部狀態相對獨立,而且不會影響其內部狀態,從而使得享元對象可以在不同的環境中被共享
缺點描述
? 復雜性增加分離內部狀態和外部狀態增加了設計復雜度
? 可能影響可讀性對于不熟悉該模式的人來說,理解代碼邏輯可能會變得困難
? 不適合頻繁變化的狀態如果大部分狀態都是外部狀態,則享元模式的優勢無法體現。為了使對象可以共享,享元模式需要將享元對象的部分狀態外部化,而讀取外部狀態將使得運行時間變長

七、與其他模式對比(補充)

模式名稱目標
單例模式確保某個類只有一個實例,并提供全局訪問點
原型模式通過克隆已有對象來創建新對象,強調復用
享元模式通過共享技術減少相似對象的創建,節約內存

八、最終小結

享元模式是一種非常有效的設計模式,特別適合那些:

  • 存在大量相似對象的應用場景;
  • 希望減少內存使用并提高性能的情況;
  • 可以明確區分內部狀態和外部狀態的對象。

掌握享元模式可以幫助你在處理大量相似對象時更加高效地管理內存資源,特別是在游戲開發、文檔處理等領域。


📌 一句話總結:

享元模式就像是一個“共享池”,把那些可以共享的部分放在一起,避免重復創建,從而達到節省內存的目的。


? 推薦使用方式:

  • 當你發現你的應用中有許多相似但獨立的對象時,考慮使用享元模式。
  • 注意合理劃分內部狀態和外部狀態,確保設計簡潔且易于維護。

九、擴展

單純享元模式和復合享元模式

單純享元模式
所有的具體享元類都是可以共享的,不存在非共享具體享元類

在這里插入圖片描述
復合享元模式
將一些單純享元對象使用組合模式加以組合
如果希望為多個內部狀態不同的享元對象設置相同的外部狀態,可以考慮使用復合享元模式

在這里插入圖片描述
部分內容由AI大模型生成,注意識別!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/89117.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/89117.shtml
英文地址,請注明出處:http://en.pswp.cn/web/89117.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

驅動開發_2.字符設備驅動

目錄1. 什么是字符設備2. 設備號2.1 設備號概念2.2 通過設備號dev分別獲取主、次設備號的宏函數2.3 主設備號的申請靜態申請動態分配2.4 注銷設備號3. 字符設備3.1 注冊字符設備3.2 注銷字符設備3.3 應用程序和驅動程序的關系3.4 file_opertaions結構體3.5 class_create3.6 創建…

直播推流技術底層邏輯詳解與私有化實現方案-以rmtp rtc hls為例-優雅草卓伊凡

直播推流技術底層邏輯詳解與私有化實現方案-以rmtp rtc hls為例-優雅草卓伊凡由于我們的甲方客戶要開始為我們項目產品上加入私有化的直播&#xff0c;這塊不得不又撿起來曾經我們做直播推流的事情了&#xff0c;其實私有化直播一直并不是一件容易的事情&#xff0c;現在大部分…

一文讀懂現代卷積神經網絡—深度卷積神經網絡(AlexNet)

目錄 深度卷積神經網絡&#xff08;AlexNet&#xff09;是什么&#xff1f; 一、AlexNet 的核心創新 1. 深度架構 2. ReLU 激活函數 3. 數據增強 4. Dropout 正則化 5. GPU 并行計算 6. 局部響應歸一化&#xff08;LRN&#xff09; 二、AlexNet 的網絡結構 三、AlexN…

JVM 垃圾收集算法全面解析

1. 引言1.1 為什么需要垃圾收集&#xff1f;在Java應用中&#xff0c;垃圾收集&#xff08;Garbage Collection&#xff0c;GC&#xff09;是一個至關重要的機制&#xff0c;它使得開發者不需要手動管理內存。與傳統的語言&#xff08;如C或C&#xff09;不同&#xff0c;Java通…

Vmware中安裝的CentOS7如何擴展硬盤大小

起初創建虛擬機時&#xff0c;大小設置不合理&#xff0c;導致我在嘗試開源項目時空間不足重新擴展硬盤&#xff0c;不僅需要在虛擬機設置中配置&#xff0c;還需要在系統內重新進行分區一、虛擬機設置打開虛擬機設置→硬盤→擴展&#xff0c;將大小設置為自己期望的大小&#…

Python+MongoDB高效開發組合

如大家所知&#xff0c;Python與MongoDB的結合是一種高效的開發組合&#xff0c;主要用于通過Python進行數據存儲、查詢及管理&#xff0c;利用MongoDB的文檔型數據庫特性實現靈活的數據處理。下面讓 Python 連接上 MongoDB&#xff1a;安裝 PyMongo&#xff1a;pip3 install p…

【論文閱讀】Masked Autoencoders Are Effective Tokenizers for Diffusion Models

introduce什么樣的 latent 空間更適合用于擴散模型&#xff1f;作者發現&#xff1a;相比傳統的 VAE&#xff0c;結構良好、判別性強的 latent 空間才是 diffusion 成功的關鍵。研究動機&#xff1a;什么才是“好的 latent 表征”&#xff1f;背景&#xff1a;Diffusion Models…

每日一SQL 【游戲玩法分析 IV】

文章目錄問題案例執行順序使用分組解決問題 案例 執行順序 SQL 語句的執行順序&#xff08;核心步驟&#xff09; 同一層級的select查詢內部, 別名在整個 SELECT 計算完成前不生效 使用分組解決 select distinct s.product_id, Product.product_name from Sales sleft join …

內部文件審計:企業文件服務器審計對網絡安全提升有哪些幫助?

企業文件服務器審計工作不僅對提升企業網絡信息安全起到重要作用&#xff0c;還能對企業內部網絡文件信息是否合規進行判斷。因此企業文件服務器審計一直被高度重視。 一、文件服務器為何成為攻擊焦點&#xff1f; 企業文件服務器通常集中存儲財務報表、人事檔案、研發資料、客…

FusionOne HCI 23 超融合實施手冊(超聚變超融合)

產品介紹 FusionOne HCI作為實現企業信息一體化的IT基礎設施平臺&#xff0c;以“軟硬件垂直深度集成和調優”、“快速部署”、“統一管理”的理念&#xff0c;提供應用融合部署&#xff0c;提升核心業務運作效率&#xff0c;降低整體采購成本。 FusionOne HCI代表了IT產品的…

AI算姻緣測算小工具流量主微信小程序開源

功能特點 響應式設計&#xff1a;完美適配各種移動設備屏幕尺寸 精美UI界面&#xff1a; 柔和的粉紅色漸變背景 圓角卡片設計 精心設計的字體和間距 愛心圖標點綴 動態效果&#xff1a; 點擊按鈕時的動畫反饋 測算結果的平滑過渡動畫 愛心漂浮動畫 進度條動態填充 AI測算功能&a…

Vue獲取上傳Excel文件內容并展示在表格中

一、安裝依賴 npm install xlsx 二、引用依賴 import XLSX from xlsx 三、代碼實現 1、注意&#xff1a;函數 analysis 中reader.readAsBinaryString(file)&#xff0c;file的數據格式如圖所示 2、示例代碼 <!-- 項目使用的前端框架為非流行框架&#xff0c;主要關注…

pipelineJob和pipeline的關系

pipelineJob與pipeline在Jenkins體系中構成配置層與執行層的協同關系,具體關聯如下: 一、核心功能定位 概念作用實現層級pipelineJob定義Job的元數據(如SCM配置、日志策略)配置層pipeline描述實際構建流程(如階段劃分、并行任務)執行層scriptPath橋梁作用:將配置層定義…

第二十篇 Word文檔自動化:Python批量生成、模板填充與內容修改,告別繁瑣排版!

python實現word 自動化重復性文檔制作&#xff0c;手動填充模板&#xff0c;效率低下還易錯1.python-docx入門&#xff1a;Word文檔的“瑞士軍刀”&#xff01;1.1 安裝與基礎概念&#xff1a;文檔、段落、運行、表格1.2 打開/創建Word文檔&#xff1a;Python與Word的初次接觸1…

【C# in .NET】7. 探秘結構體:值類型的典型代表

探秘結構體&#xff1a;值類型的典型代表 在 C# 的類型系統中&#xff0c;結構體&#xff08;Struct&#xff09;作為值類型的典型代表&#xff0c;一直扮演著既基礎又微妙的角色。許多開發者在日常編碼中雖頻繁使用結構體&#xff08;如int、DateTime等&#xff09;&#xff0…

深入探討Hadoop YARN Federation:架構設計與實踐應用

Hadoop YARN Federation簡介基本概念與設計初衷Hadoop YARN Federation作為Apache Hadoop 3.x版本的核心特性之一&#xff0c;其本質是通過多集群聯合管理機制突破單點資源管理器的性能瓶頸。傳統YARN架構中&#xff0c;單個ResourceManager&#xff08;RM&#xff09;需要管理…

STM32固件升級設計——SD卡升級固件

目錄 概述 一、功能描述 1、BootLoader部分&#xff1a; 2、APP部分&#xff1a; 二、BootLoader程序制作 1、分區定義 2、 主函數 3、SD卡升級文件檢測和更新 4、程序跳轉 三、APP程序制作 四、工程配置&#xff08;默認KEIL5&#xff09; 五、運行測試 結束語…

基于Python的圖像文字識別系統

主要語言&#xff1a;Python數據庫&#xff1a;SQLiteUI界面&#xff1a;PYQT5文字識別模型&#xff1a;Tesseract OCR&#xff08;本地搭建&#xff09;主要功能&#xff1a;登錄注冊&#xff1a;登錄注冊功能。圖片管理&#xff1a;單張/多張上傳、圖片列表、預覽、刪除、切換…

028_分布式部署架構

028_分布式部署架構 概述 本文檔介紹如何設計和實現Claude應用的分布式部署架構&#xff0c;包括負載均衡、緩存策略、服務發現、容錯機制等。 微服務架構設計 1. 服務拆分策略 from abc import ABC, abstractmethod from typing import Dict, Any, Optional import asyncio im…

duckdb和pyarrow讀寫arrow格式的方法

arrow格式被多種分析型數據引擎廣泛采用&#xff0c;如datafusion、polars。duckdb有一個arrow插件&#xff0c;原來是core插件&#xff0c;1.3版后被廢棄&#xff0c;改為社區級插件&#xff0c;名字改為nanoarrow, 別名還叫arrow。 安裝 D install arrow from community; D…