設計模式——享元設計模式(結構型)

摘要

享元設計模式是一種結構型設計模式,旨在通過共享對象減少內存占用和提升性能。其核心思想是將對象狀態分為內部狀態(可共享)和外部狀態(不可共享),并通過享元工廠管理共享對象池。享元模式包含抽象享元類、具體享元類、非共享具體享元類和享元工廠類。它適用于處理大量相似對象的場景,如文檔編輯器中的字符對象。文章還提供了享元模式的實現方式、適合與不適合的使用場景、實戰示例以及與其他設計模式的比較。

1. 享元設計模式定義

享元設計模式(Flyweight Pattern) 是一種結構型設計模式,用于減少對象的數量,以節省內存和提高性能。享元模式通過共享內存中已經存在的對象,避免重復創建相同內容的對象,適用于大量相似對象的場景。

1.1.1. 核心思想

把對象狀態劃分為:

  • 內部狀態(可共享,存儲在享元對象中)
  • 外部狀態(不可共享,由客戶端維護)

享元工廠(FlyweightFactory): 負責管理共享對象的池,復用已有實例。

1.1.2. 舉例說明

假設一個文檔編輯器中有 10 萬個字符,但字符集就 128 個(ASCII),如果每個字符都是獨立對象,將浪費大量內存。此時可以:

  • 把字符內容作為內部狀態(可共享)
  • 把字體、大小、顏色作為外部狀態(由外部控制)
  • 同一個字符內容只創建一次對象,由享元工廠復用

2. 享元設計模式結構

享元模式包含如下角色:

  • Flyweight: 抽象享元類
  • ConcreteFlyweight: 具體享元類
  • UnsharedConcreteFlyweight: 非共享具體享元類
  • FlyweightFactory: 享元工廠類

2.1. 享元設計模式類圖

2.2. 享元設計模式時序圖

3. 享元設計模式實現方式

享元設計模式的實現方式主要圍繞 對象共享池內部狀態與外部狀態的分離。它通過一個享元工廠(FlyweightFactory)來管理共享對象,避免重復創建,從而節省內存。

3.1. 1?? 定義抽象享元接口(Flyweight)

public interface Flyweight {void operation(String externalState); // 外部狀態由調用方傳入
}

3.2. 2?? 創建具體享元類(ConcreteFlyweight)

public class ConcreteFlyweight implements Flyweight {private final String intrinsicState; // 內部狀態,能共享public ConcreteFlyweight(String intrinsicState) {this.intrinsicState = intrinsicState;}@Overridepublic void operation(String externalState) {System.out.println("共享[" + intrinsicState + "],非共享[" + externalState + "]");}
}

3.3. 3?? 創建享元工廠類(FlyweightFactory)

public class FlyweightFactory {private final Map<String, Flyweight> pool = new HashMap<>();public Flyweight getFlyweight(String key) {if (!pool.containsKey(key)) {pool.put(key, new ConcreteFlyweight(key));}return pool.get(key);}public int getPoolSize() {return pool.size();}
}

3.4. 4?? 客戶端調用(Client)

public class Client {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight a1 = factory.getFlyweight("A");Flyweight a2 = factory.getFlyweight("A"); // 重復,不創建新對象a1.operation("外部狀態1");a2.operation("外部狀態2");System.out.println("共享對象數量:" + factory.getPoolSize()); // 輸出:1}
}

3.5. ? 享元設計模式總結

要素

說明

內部狀態

可以共享,存儲在享元對象中,如字符、類型

外部狀態

每次調用時由客戶端傳入,不在享元內部存儲

工廠類

管理共享對象的創建與復用

緩存池

使用 HashMap 或 ConcurrentHashMap 存儲共享對象

線程安全注意點

在并發環境下需保證工廠創建邏輯線程安全(如加鎖或使用 ConcurrentMap)

4. 享元設計模式適合場景

4.1. ? 適合使用享元設計模式的場景

場景

說明

大量重復對象需創建,且狀態大部分相同

比如:文字編輯器中的字符對象、地圖中的草地格子、游戲中的粒子等,能顯著減少內存開銷。

對象創建成本高,希望通過復用來減少系統負擔

比如:金融系統中共享的“黑名單規則”、“風控維度元數據”等。

對象狀態可拆分為可共享的內部狀態和不可共享的外部狀態

共享部分放入享元,變動部分交給外部傳入,從而實現高復用。

系統中存在大量細粒度對象,結構相似、功能一致

如圖形系統中的形狀節點、文檔編輯器中的字體、格式等。

緩存池或對象池機制的實現場景

比如:數據庫連接池、線程池、元數據池、圖標池、緩存字典等。

4.2. ? 不適合使用享元模式的場景

場景

原因

對象狀態頻繁變化,不可拆分共享/不共享狀態

對象狀態不能被提取為外部狀態時,就無法有效共享,甚至會導致共享污染。

對象之間差異太大,無法復用或沒有可共享部分

如果每個對象都是完全不同的個體,享元模式無法帶來價值。

系統對對象獨立性要求高,不允許共享

比如線程不安全或敏感業務邏輯要求每個對象獨立維護生命周期。

對象數量本身不多,內存開銷可以接受

引入享元結構反而增加了系統復雜度、調試難度,不值得。

共享對象內部包含資源引用,如 Socket、File 等

資源不允許多個業務共享,使用享元會造成資源沖突或數據錯亂。

4.3. 🧠 享元模式的場景總結

項目

適合使用享元模式

不適合使用享元模式

對象數量

? 大量重復

? 數量少

狀態結構

? 可拆分內外狀態

? 狀態復雜或強耦合

系統壓力

? 內存敏感,需優化

? 性能足夠,優化收益低

可復用性

? 可復用部分明顯

? 無明顯共享邏輯

系統復雜度

? 有控制成本價值

? 小項目/臨時代碼

5. 享元設計模式實戰示例

下面是一個 享元設計模式在金融風控系統中的 Spring 實戰示例,場景為:風控規則元數據共享池,用于緩存和復用規則的靜態定義,減少重復加載和內存占用。在風控系統中,不同的策略規則經常引用相同的“規則定義”(如規則編號、描述、字段映射等)。這些定義是 不可變 的、重復使用 的,適合使用享元模式來緩存復用。

5.1. ? 享元接口:RuleDefinition

public interface RuleDefinition {void evaluate(String param);  // 示例行為
}

5.2. ? 具體享元類:ConcreteRuleDefinition

public class ConcreteRuleDefinition implements RuleDefinition {private final String ruleCode;private final String description;public ConcreteRuleDefinition(String ruleCode, String description) {this.ruleCode = ruleCode;this.description = description;}@Overridepublic void evaluate(String param) {System.out.println("執行規則 [" + ruleCode + "] - " + description + ",參數:" + param);}public String getRuleCode() {return ruleCode;}public String getDescription() {return description;}
}

5.3. ? 享元工廠:RuleDefinitionFactory(由 Spring 管理)

@Component
public class RuleDefinitionFactory {private final Map<String, RuleDefinition> pool = new ConcurrentHashMap<>();/*** 獲取共享的規則定義*/public RuleDefinition getRule(String ruleCode) {return pool.computeIfAbsent(ruleCode, this::loadRuleDefinition);}/*** 模擬從數據庫或配置中加載規則元數據*/private RuleDefinition loadRuleDefinition(String ruleCode) {// 實際情況應從數據庫或配置中心加載System.out.println("加載規則定義:" + ruleCode);return new ConcreteRuleDefinition(ruleCode, "規則描述_" + ruleCode);}public int getPoolSize() {return pool.size();}
}

5.4. ? 客戶端服務:RiskEngineService(注入使用)

@Service
public class RiskEngineService {@Autowiredprivate RuleDefinitionFactory ruleDefinitionFactory;public void processRisk(String ruleCode, String inputParam) {RuleDefinition rule = ruleDefinitionFactory.getRule(ruleCode);rule.evaluate(inputParam);}
}

5.5. ? 啟動類或控制器測試(模擬調用)

@RestController
public class RiskController {@Autowiredprivate RiskEngineService riskEngineService;@GetMapping("/risk/test")public String test() {riskEngineService.processRisk("R001", "用戶A數據");riskEngineService.processRisk("R001", "用戶B數據");riskEngineService.processRisk("R002", "用戶C數據");return "風控規則執行完畢";}
}

5.6. ? 運行結果示例

加載規則定義:R001
執行規則 [R001] - 規則描述_R001,參數:用戶A數據
執行規則 [R001] - 規則描述_R001,參數:用戶B數據
加載規則定義:R002
執行規則 [R002] - 規則描述_R002,參數:用戶C數據

可見:R001 只加載一次,后續復用;實現了享元模式在 Spring 項目下的實戰落地。

要素

說明

享元類

ConcreteRuleDefinition

享元工廠

RuleDefinitionFactory

共享池

ConcurrentHashMap緩存規則

注解注入

使用 @Autowired,不采用構造函數注入

應用場景

風控規則元數據復用、規則模板復用、評分模型共享

6. 享元設計模式思考

6.1. 享元設計模式與原型設計模式?

享元設計模式(Flyweight)和原型設計模式(Prototype)都是創建相關的設計模式,但它們解決的問題、使用方式和結構完全不同。下面是它們的詳細對比:

6.1.1. 🆚 Flyweight vs. Prototype

維度

享元模式(Flyweight)

原型模式(Prototype)

💡 設計模式類型

結構型模式

創建型模式

🎯 目的

通過共享對象來減少內存占用和對象數量

通過復制已有對象來創建新對象,避免 new 開銷

📦 關注點

對象復用與共享,分離內部狀態與外部狀態

快速創建新對象(特別是復雜結構)

🧠 實現機制

享元工廠維護共享對象池,通過傳入外部狀態來復用對象

使用 clone()

方法或拷貝構造函數復制現有對象

📂 狀態管理

內部狀態共享,外部狀態由使用方維護

完整復制所有狀態(深拷貝/淺拷貝)

📈 適合場景

- 大量重復對象,如文字編輯器中的字符對象
- 數據緩存池
- 游戲中的粒子/地圖圖塊共享

- 克隆原型對象
- 對象構造成本高(如樹狀結構)

🧩 示例

String Pool、Integer.valueOf、數據庫連接池

原型注冊器、工作流模板復制、前端組件克隆等

?? 使用注意點

- 內外部狀態劃分要明確
- 不適合狀態頻繁變化對象

- 深拷貝需注意引用類型對象

6.1.2. ? 簡單總結

  • 享元模式 = 節省內存、共享對象:適用于大量對象重復的場景。
  • 原型模式 = 快速復制、提升性能:適用于快速創建復雜對象的場景。

6.1.3. 📌 舉個類比:

類比

描述

享元

比如一個圖書館的“圖書”是共享的,用戶借用的是引用,圖書本身不復制。

原型

比如一個表格模板,每次創建新文檔都是復制模板,然后修改。

博文參考

  • 5. 享元模式 — Graphic Design Patterns
  • 享元設計模式

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

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

相關文章

互聯網大廠Java求職面試:云原生微服務架構設計與AI大模型集成實戰

互聯網大廠Java求職面試&#xff1a;云原生微服務架構設計與AI大模型集成實戰 面試場景設定 人物設定&#xff1a; 李明&#xff08;技術總監&#xff09;&#xff1a;擁有15年分布式系統架構經驗&#xff0c;主導過多個億級用戶系統的重構&#xff0c;對云原生和AI融合有深…

nginx+tomcat動靜分離、負載均衡

一、理論 nginx用于處理靜態頁面以及做調度器&#xff0c;tomcat用于處理動態頁面 lvs&#xff08;四層&#xff09; 輪詢&#xff08;rr&#xff09; 加權輪詢&#xff08;wrr&#xff09; 最小連接&#xff08;lc&#xff09; 加權最小連接&#xff08;wlc&#xff09; ngi…

什么是AI芯片?

首先&#xff0c;我們要了解一下&#xff1a;什么是芯片&#xff1f;芯片的本質就是在半導體襯底上制作能實現一系列特定功能的集成電路。 其次&#xff0c;來看一下AI的概念。AI是研究如何使計算機能夠模擬和執行人類智能任務的科學和技術領域&#xff0c;致力于開發能夠感知…

PostgreSQL數據庫配置SSL操作說明書

背景&#xff1a; 因為postgresql或者mysql目前通過docker安裝&#xff0c;只需要輸入主機IP、用戶名、密碼即可訪問成功&#xff0c;這樣其實是不安全的&#xff0c;可能會通過一些手段獲取到用戶名密碼導致數據被竊取。而ES、kafka等也是通過用戶名/密碼方式連接&#xff0c;…

k8s更新證書

[rootk8s-master01 ~]# sudo kubeadm certs renew all [renew] Reading configuration from the cluster… [renew] FYI: You can look at this config file with ‘kubectl -n kube-system get cm kubeadm-config -o yaml’ certificate embedded in the kubeconfig file for…

正點原子lwIP協議的學習筆記

正點原子lwIP協議的學習筆記 正點原子lwIP學習筆記——lwIP入門 正點原子lwIP學習筆記——MAC簡介 正點原子lwIP學習筆記——PHY芯片簡介 正點原子lwIP學習筆記——以太網DMA描述符 正點原子lwIP學習筆記——裸機移植lwIP 正點原子lwIP學習筆記——裸機lwIP啟動流程 正點…

MongoTemplate常用api學習

本文只介紹常用的api&#xff0c;盡量以最簡單的形式學會mongoTemplate基礎api的使用 一、新增 主要包含三個api&#xff1a;insert&#xff08;一個或遍歷插多個&#xff09;、insertAll&#xff08;批量多個&#xff09;、save&#xff08;插入或更新&#xff09; //這里簡…

006網上訂餐系統技術解析:打造高效便捷的餐飲服務平臺

網上訂餐系統技術解析&#xff1a;打造高效便捷的餐飲服務平臺 在數字化生活方式普及的當下&#xff0c;網上訂餐系統成為連接餐飲商家與消費者的重要橋梁。該系統以菜品分類、訂單管理等模塊為核心&#xff0c;通過前臺展示與后臺錄入的分工協作&#xff0c;為管理員和會員提…

網絡攻防技術五:網絡掃描技術

文章目錄 一、網絡掃描的基礎概念二、主機發現三、端口掃描1、端口號2、端口掃描技術3、端口掃描隱秘策略 四、操作系統識別五、漏洞掃描六、簡答題1. 主機掃描的目的是什么&#xff1f;請簡述主機掃描方法。2. 端口掃描的目的是什么&#xff1f;請簡述端口掃描方法及掃描策略。…

生成JavaDoc文檔

生成 JavaDoc 文檔 1、快速生成 文檔 注解 2、常見的文檔注解 3、腳本生成 doc 文檔 4、IDEA工具欄生成 doc 文檔 第一章 快速入門 第01節 使用插件 在插件工具當中&#xff0c;找到插件 javaDoc 使用方式&#xff0c;在代碼區域&#xff0c;直接點擊右鍵。選擇 第02節 常用注…

大數據-276 Spark MLib - 基礎介紹 機器學習算法 Bagging和Boosting區別 GBDT梯度提升樹

點一下關注吧&#xff01;&#xff01;&#xff01;非常感謝&#xff01;&#xff01;持續更新&#xff01;&#xff01;&#xff01; 大模型篇章已經開始&#xff01; 目前已經更新到了第 22 篇&#xff1a;大語言模型 22 - MCP 自動操作 FigmaCursor 自動設計原型 Java篇開…

【HarmonyOS 5】如何優化 Harmony-Cordova 應用的性能?

以下是針對 ?Harmony-Cordova 應用性能優化?的完整方案&#xff0c;結合鴻蒙原生特性和Cordova框架優化策略&#xff1a; ??一、渲染性能優化? ?減少布局嵌套層級? 使用扁平化布局&#xff08;如 Grid、GridRow&#xff09;替代多層 Column/Row 嵌套&#xff0c;避免冗…

數據庫管理-第332期 大數據已死,那什么當立?(20250602)

數據庫管理332期 2025-06-02 數據庫管理-第332期 大數據已死&#xff0c;那什么當立&#xff1f;&#xff08;20250602&#xff09;1 概念還是技術2 必然的大數據量3 離線到實時4 未來總結 數據庫管理-第332期 大數據已死&#xff0c;那什么當立&#xff1f;&#xff08;202506…

相機--RGBD相機

教程 分類原理和標定 原理 視頻總結 雙目相機和RGBD相機原理 作用 RGBD相機RGB相機深度&#xff1b; RGB-D相機同時獲取兩種核心數據&#xff1a;RGB彩色圖像和深度圖像&#xff08;Depth Image&#xff09;。 1. RGB彩色圖像 數據格式&#xff1a; 標準三通道矩陣&#…

神經符號集成-三篇綜述

講解三篇神經符號集成的綜述&#xff0c;這些綜述沒有針對推薦系統的&#xff0c;所以大致過一下&#xff0c;下一篇帖子會介紹針對KG的兩篇綜述。綜述1關注的是系統集成和數據流的宏觀模式“是什么”&#xff1b;綜述3關注的是與人類理解直接相關的中間過程和決策邏輯的透明度…

window/linux ollama部署模型

模型部署 模型下載表: deepseek-r1 win安裝ollama 注意去官網下載ollama,這個win和linux差別不大,win下載exe linux安裝ollama 采用docker方式進行安裝: OLLAMA_HOST=0.0.0.0:11434 \ docker run -d \--gpus all \-p 11434:11434 \--name ollama \-v ollama:/root/.ol…

計算A圖片所有顏色占B圖片紅色區域的百分比

import cv2 import numpy as npdef calculate_overlap_percentage(a_image_path, b_image_path):# 讀取A組和B組圖像a_image cv2.imread(a_image_path)b_image cv2.imread(b_image_path)# 將圖像從BGR轉為HSV色彩空間&#xff0c;便于顏色篩選a_hsv cv2.cvtColor(a_image, c…

每日算法 -【Swift 算法】盛最多水的容器

盛最多水的容器&#xff1a;Swift 解法與思路分析 &#x1f4cc; 問題描述 給定一個長度為 n 的整數數組 height&#xff0c;每個元素表示在橫坐標 i 處的一條垂直線段的高度。任意兩條線段和 x 軸構成一個容器&#xff0c;該容器可以裝水&#xff0c;水量的大小由較短的那條…

云原生安全基礎:Linux 文件權限管理詳解

&#x1f525;「炎碼工坊」技術彈藥已裝填&#xff01; 點擊關注 → 解鎖工業級干貨【工具實測|項目避坑|源碼燃燒指南】 在云原生環境中&#xff0c;Linux 文件權限管理是保障系統安全的核心技能之一。無論是容器化應用、微服務架構還是基礎設施即代碼&#xff08;IaC&#xf…

TypeScript 中的字面量類型(Literal Types)

在 TypeScript 中&#xff0c;字面量類型&#xff08;Literal Types&#xff09;是一種特殊的類型&#xff0c;它允許你將變量的類型限制為某個具體的值&#xff08;如特定的字符串、數字或布爾值&#xff09;&#xff0c;而不僅僅是寬泛的類型&#xff08;如 string、number&a…