21.享元模式:思考與解讀

原文地址:享元模式:思考與解讀? 更多內容請關注:深入思考與解讀設計模式

引言

在軟件開發中,特別是當你處理大量相似對象時,是否會遇到一個問題:大量的對象會占用大量的內存,而這些對象有許多相同的狀態?你是否覺得,重復創建相似的對象會浪費內存,并且增加系統的負擔?是否有一種方式,可以通過共享相同的對象來節約內存,并提高系統的效率?

享元模式正是為了解決這一問題而設計的。它通過共享相同的對象來減少內存的使用,特別是在對象的狀態中有很多相同部分時。你是否理解,為什么通過共享對象的不可變部分,可以顯著降低內存開銷?

在本文中,我們將通過一系列問題,逐步引導你理解享元模式的核心思想、應用場景以及如何實現它。

什么是享元模式?

問題1:當你需要創建大量對象時,是否曾遇到過內存占用過大的問題?你通常如何處理大量相似對象的創建?

假設你有一個系統,需要創建大量的對象,每個對象有相同的部分狀態。你是如何管理這些對象的?是否每個對象都創建一個新實例,還是有其他的方式來減少內存開銷?

問題2:如果有一種方式,能夠在創建大量相似對象時,只保留不同的部分,而共享相同的部分,這樣是否能顯著減少內存的使用?

享元模式通過共享相同的對象部分,只保留對象的可變部分來節約內存。你是否理解,為什么這種方式能夠提高內存的利用率,尤其是在對象中存在大量相同狀態時?

享元模式的核心概念

問題3:享元模式通常包含哪些角色?每個角色的職責是什么?

享元模式通常包含以下幾個角色:

  1. 享元(Flyweight):定義了共享對象的接口,并可以通過外部傳入的狀態來進行區分。

  2. 具體享元(ConcreteFlyweight):實現享元接口,提供共享對象的具體實現。

  3. 享元工廠(FlyweightFactory):負責管理享元對象的創建和共享,確保相同的享元對象只創建一次。

  4. 客戶端(Client):使用享元對象,并將外部狀態傳遞給共享對象。

你能理解這些角色是如何協同工作的?它們如何通過共享相同的對象來減少內存的占用?

問題4:為什么要引入享元工廠來管理享元對象的創建和共享?這種方式如何保證享元對象的復用?

享元工廠負責創建和管理享元對象,確保相同的對象只創建一次。你是否理解,為什么享元工廠能夠有效地減少對象的重復創建,從而提高系統的效率和內存使用?

問題5:享元模式是如何區分對象的內部狀態和外部狀態的?為什么享元對象的內部狀態是共享的,而外部狀態是獨立的?

享元模式通過將不可變的部分(內部狀態)共享,而將可變的部分(外部狀態)保留在客戶端。你是否理解,為什么內部狀態可以共享,而外部狀態需要傳遞給享元對象?

享元模式的實現

假設我們正在開發一個圖形編輯系統,其中每個圖形都有相同的顏色和形狀,但可能有不同的位置。我們將使用享元模式來共享相同顏色和形狀的圖形實例,從而節約內存。

步驟1:定義享元接口

from abc import ABC, abstractmethodclass Shape(ABC):@abstractmethoddef draw(self, x: int, y: int):pass

問題6:為什么我們需要定義一個享元接口(Shape)?它的作用是什么?

Shape接口定義了所有具體享元類需要實現的draw()方法,使得客戶端能夠使用統一的接口來操作不同類型的圖形。你能理解,為什么通過接口來定義共享對象的行為,可以讓享元模式更加靈活?

步驟2:定義具體享元類
class Circle(Shape):def __init__(self, color: str):self.color = color  # 共享的內部狀態def draw(self, x: int, y: int):print(f"Drawing a {self.color} circle at ({x}, {y})")

問題7:Circle類是如何實現Shape接口的?它如何管理內部狀態?

Circle類實現了Shape接口,并通過color來管理共享的內部狀態。你是否理解,為什么圖形的顏色可以作為共享的狀態,而位置是獨立的?

步驟3:定義享元工廠類
class ShapeFactory:def __init__(self):self._shapes = {}def get_shape(self, color: str) -> Shape:if color not in self._shapes:self._shapes[color] = Circle(color)return self._shapes[color]

問題8:ShapeFactory類是如何管理享元對象的?它如何確保相同顏色的圖形只創建一次?

ShapeFactory類通過維護一個享元對象池(_shapes字典),確保相同顏色的圖形只創建一次。你是否理解,為什么這種方式能夠有效避免重復創建相同的享元對象?

步驟4:客戶端代碼
def main():factory = ShapeFactory()# 獲取不同位置的相同顏色的圓形shape1 = factory.get_shape("red")shape2 = factory.get_shape("blue")shape3 = factory.get_shape("red")shape1.draw(10, 20)  # Drawing a red circle at (10, 20)shape2.draw(30, 40)  # Drawing a blue circle at (30, 40)shape3.draw(50, 60)  # Drawing a red circle at (50, 60)print(f"shape1 and shape3 are the same object: {shape1 is shape3}")  # Trueif __name__ == "__main__":main()

問題9:在客戶端代碼中,如何通過享元工廠來獲取享元對象?為什么相同顏色的圖形對象只會創建一次?

客戶端通過ShapeFactory來獲取享元對象,并且相同顏色的圖形對象會復用。你是否理解,為什么這種方式讓圖形對象的內存占用變得更加高效,且避免了重復創建相同對象?

享元模式的優缺點

問題10:享元模式的優點是什么?它如何幫助我們節省內存,并提高系統的性能?

享元模式通過共享相同的狀態來減少內存的占用,尤其是在處理大量相似對象時。你是否理解,這種方式如何顯著提高內存的利用率,并且提高系統的性能?

問題11:享元模式的缺點是什么?它是否可能導致客戶端的復雜性增加?

盡管享元模式能夠節約內存,但它也可能導致客戶端需要管理外部狀態,增加客戶端的復雜性。你是否認為,在某些情況下,享元模式可能會讓系統的設計變得更加復雜?如何在使用享元模式時平衡內存節約與代碼復雜性?

適用場景

問題12:享元模式適用于哪些場景?

享元模式特別適用于以下場景:

  • 當你需要處理大量相似對象,而這些對象有相同的不可變狀態時。

  • 當對象狀態中有大量重復的部分,可以共享時。

  • 當需要在大量對象中減少內存占用時。

你能想到其他類似的場景嗎?例如,游戲中的紋理對象、圖形界面組件的共享等,是否也可以使用享元模式?

問題13:享元模式是否適用于所有場景?在某些情況下,是否有更合適的設計模式來替代享元模式?

享元模式適用于需要共享狀態的場景,但如果對象的狀態非常復雜,且每個對象的狀態差異較大,是否可以考慮使用其他設計模式?例如,工廠模式、策略模式等,是否可能更適合一些場景?

接下來,我們將通過具體的代碼示例來加深理解享元模式。

享元模式深入解讀

一、引言

享元模式(Flyweight Pattern)是一種結構型設計模式,旨在通過共享對象來有效地支持大量的細粒度對象。享元模式通過減少對象的創建數量來優化內存使用,尤其在需要創建大量相似對象時非常有用。該模式通過將相同的數據部分共享來減少冗余,從而節省內存,提高性能。


二、簡單理解:什么是享元模式?

1. 什么是享元模式?

享元模式的核心思想是:通過共享已經存在的對象來減少內存的使用,尤其是當大量對象具有相同的狀態時。享元模式把對象的狀態分為兩類:內部狀態和外部狀態。內部狀態是對象的固定部分,可以共享;外部狀態是對象的可變部分,不能共享。

通俗地講,享元模式就像是你在使用一個圖標庫。當你需要多個相似的圖標時,你并不為每個圖標創建一個新的實例,而是共享相同的圖標對象,只有在需要時,才為每個圖標設置不同的位置(外部狀態)。這樣做不僅節省了內存,也提高了性能。

2. 享元模式的組成部分

享元模式通常包含以下幾個部分:

  • 享元接口(Flyweight):定義共享對象的接口,通常包括設置和獲取外部狀態的方法。

  • 具體享元類(ConcreteFlyweight):實現享元接口,并存儲共享的內部狀態。

  • 享元工廠類(FlyweightFactory):負責創建并管理享元對象,確保共享對象的唯一性。

  • 外部狀態(Extrinsic State):不需要共享的狀態,通常由客戶端來管理。


三、用自己的話解釋:如何理解享元模式?

1. 類比實際生活中的場景

假設你有很多相似的產品(例如,多個相同型號的手機),每個手機的外觀和功能是相同的,但它們可能具有不同的顏色、存儲容量等特征。你并不需要為每個手機實例創建一個新的外觀對象,而是可以共享相同的外觀對象,只為每個手機單獨存儲顏色和容量等信息(外部狀態)。通過這種方式,你可以減少內存使用,提高系統的效率。

在編程中,享元模式通過共享對象的內部狀態,避免了重復創建相似的對象,并通過外部狀態為每個對象提供個性化的特征,從而節省了內存。

2. 為什么要使用享元模式?

使用享元模式的好處是,它通過共享對象來減少內存使用,尤其是在需要大量相似對象的情況下,享元模式能夠顯著提高系統的性能。它將不可共享的部分和可共享的部分分開,確保只有外部狀態是可變的,而共享對象的內部狀態是固定的。


四、深入理解:享元模式的實現

接下來,我們通過一個具體的代碼示例來實現享元模式,幫助你更好地理解如何在代碼中使用這個模式。

示例:文字繪制系統

假設我們正在開發一個文字繪制系統,其中有多個相同字體和大小的文字對象,但它們的顏色和位置可能不同。我們可以通過享元模式來共享相同的文字對象,避免為每個文字都創建一個新的對象,從而節省內存。

1. 定義享元接口
# 享元接口:定義共享對象的接口
class Flyweight:def draw(self, color: str, position: tuple):pass
2. 定義具體享元類
# 具體享元類:表示一個共享的文字對象
class ConcreteFlyweight(Flyweight):def __init__(self, font: str, size: int):self.font = fontself.size = sizedef draw(self, color: str, position: tuple):print(f"Drawing text with font {self.font}, size {self.size}, color {color}, at position {position}")
3. 定義享元工廠類
# 享元工廠類:負責創建并管理享元對象
class FlyweightFactory:def __init__(self):self._flyweights = {}def get_flyweight(self, font: str, size: int):key = f"{font}-{size}"if key not in self._flyweights:print(f"Creating new Flyweight object for font {font} and size {size}")self._flyweights[key] = ConcreteFlyweight(font, size)return self._flyweights[key]
4. 客戶端代碼:使用享元模式繪制文字
# 客戶端代碼:使用享元模式繪制多個文字對象
factory = FlyweightFactory()# 獲取共享的文字對象
text1 = factory.get_flyweight("Arial", 12)
text2 = factory.get_flyweight("Arial", 12)  # 這將復用之前的對象
text3 = factory.get_flyweight("Times New Roman", 14)# 繪制文字,設置不同的外部狀態(顏色和位置)
text1.draw("Red", (10, 20))
text2.draw("Blue", (15, 25))
text3.draw("Green", (30, 35))# 輸出:
# Creating new Flyweight object for font Arial and size 12
# Drawing text with font Arial, size 12, color Red, at position (10, 20)
# Drawing text with font Arial, size 12, color Blue, at position (15, 25)
# Creating new Flyweight object for font Times New Roman and size 14
# Drawing text with font Times New Roman, size 14, color Green, at position (30, 35)
代碼解析:
  1. Flyweight?類:這是享元接口,定義了所有共享對象的公共方法。所有具體的享元類都必須實現這個接口,并定義如何繪制文字。

  2. ConcreteFlyweight?類:這是具體的享元類,表示具有固定字體和大小的文字對象。它實現了?draw?方法,根據外部狀態(如顏色和位置)來繪制文字。

  3. FlyweightFactory?類:這是享元工廠類,負責創建和管理享元對象。工廠類緩存已經創建的享元對象,避免重復創建相同的對象。當客戶端請求某個特定字體和大小的文字對象時,工廠會檢查是否已有該對象,如果有則返回已存在的對象。

  4. 客戶端代碼:客戶端通過享元工廠獲取共享的文字對象,然后設置不同的外部狀態(顏色和位置)來繪制文字。多個相同字體和大小的文字對象共享同一個享元對象,只在顏色和位置等外部狀態上有所不同。


五、解釋給別人:如何講解享元模式?

1. 用簡單的語言解釋

享元模式就像是你有一堆相似的物品(例如多個相同的圖標、文字等),而你不需要為每個物品創建一個新的實例。你可以通過共享相同的物品對象,只為每個物品設置不同的特征(外部狀態)。這樣,你可以節省大量內存,避免重復創建相似的對象。

2. 為什么要使用享元模式?

使用享元模式的好處是,它可以顯著減少內存使用,尤其是在需要創建大量相似對象的情況下。通過共享對象的內部狀態,只有在需要的時候,才為每個對象設置不同的外部狀態,從而節省了資源。此外,享元模式也提高了系統的性能,特別是在圖形界面和游戲開發等需要大量重復對象的場景中。


六、總結

享元模式通過共享不可變的狀態,顯著減少了內存開銷,并提高了系統的效率,尤其在大量相似對象的場景中。然而,享元模式也可能增加客戶端的復雜性,尤其是當外部狀態需要管理時。

通過以上學習過程,我們可以得出以下結論:

  • 享元模式?通過共享相同的對象來減少內存使用,尤其是在需要大量相似對象時非常有用。它將對象的內部狀態和外部狀態分開,共享內部狀態,外部狀態由客戶端管理。

  • 享元模式適用于那些需要大量相似對象且對象狀態可分為共享和非共享部分的場景,如圖形界面中的圖標、文字、粒子等。

享元模式的優點:
  • 節省內存:通過共享相同的對象,減少了內存的使用。

  • 提高性能:減少了對象的創建和銷毀,提高了系統的性能。

  • 靈活性:通過將內部狀態和外部狀態分離,使得對象的狀態管理更加靈活。

享元模式的缺點:
  • 增加復雜性:享元模式引入了共享對象和外部狀態的管理,可能導致代碼的復雜度

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

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

相關文章

java方法重寫學習筆記

方法重寫介紹 子類和父類有兩個返回值,參數,名稱都一樣的方法, 子類的方法會覆蓋父類的方法。 調用 public class Overide01 {public static void main(String[] args) {Dog dog new Dog();dog.cry();} }Animal類 public class Animal {…

什么是ESLint?它有什么作用?

ESLint 是一個用于 靜態代碼分析 的工具,專門檢測 JavaScript/TypeScript 代碼中的潛在問題和風格違規。它通過預定義的規則集幫助開發者保持代碼的一致性和質量,是前端工程化的核心工具之一。 一、ESLint 的核心作用 1. 錯誤檢查(Error Detection) 識別語法錯誤、未定義變…

Docker的網絡介紹

網絡簡單介紹 在介紹 Docker 的網絡模式之前,先簡單說下我們在使用 Vmware 虛擬機中的網絡模式,形成對比,更好理解。 1、Vmware 中的網絡模式 1.1、VMnet0(橋接模式) 虛擬機通過宿主機的物理網卡直接連接到外部網絡…

Netty學習專欄(六):深度解析Netty核心參數——從參數配置到生產級優化

文章目錄 前言一、核心參數全景解析1.1 基礎網絡層參數1.2 內存管理參數1.3 水位線控制1.4 高級參數與系統級優化 二、生產級優化策略2.1 高并發場景優化2.2 低延遲場景優化 總結 前言 在分布式系統和高并發場景中,Netty作為高性能網絡通信框架的核心地位無可替代。…

計算機網絡學習(六)——UDP

一、UDP UDP(User Datagram Protocol,用戶數據報協議)是傳輸層的一種協議,和 TCP 并列。與 TCP 不同,UDP 是無連接、不可靠、面向報文的協議,它的設計目標是追求更快的數據傳輸速度和更小的開銷。 UDP 為…

vue3文本超出三行顯示省略號,點擊查看更多顯示全部文本

只有一行時&#xff08;不顯示展開按鈕&#xff09;&#xff1a; 話不多說&#xff0c;上碼 ~template <el-col :span"24"><el-form-item :label"$t(warningOrgNames_)"><div class"content-box" ref"contanierRef"…

手寫Tomcat(一)

一、Tomcat簡介 Tomcat 服務器是一個免費的開放源代碼的Web應用服務器&#xff0c;屬于輕量級應用服務器&#xff0c;在中小型系統和并發訪問用戶不是很多的場合下被普遍使用&#xff0c;是開發和調試JSP 程序的首選。 1.1 Tomcat基本架構 Servlet接口文件中定義的方法有以下…

第三節_PySide6中Qt Designer 的基礎使用_上篇

文章目錄 前言一、Qt Designer簡介1.什么是 Qt Designer&#xff1f;2.核心功能3.核心優勢 二、Qt Designer界面介紹1.主窗口的創建2.窗口五大區域的簡單介紹 三、界面布局 Layout1.窗口布局方式介紹2.UI布局技巧概述3.UI布局實戰應用 總結 前言 第二節_PySide6項目創建流程介…

行列式的線性性質(僅限于單一行的加法拆分)

當然可以&#xff0c;以下是經過排版優化后的內容&#xff0c;保持了原始內容不變&#xff0c;僅調整了格式以提升可讀性&#xff1a; 行列式的線性性質&#xff08;加法拆分&#xff09; 這個性質說的是&#xff1a;如果行列式的某一行&#xff08;或某一列&#xff09;的所有…

Git使用說明

配置Git 確定已經安裝了Git, 通過以下的命令配置全局的郵箱和用戶名 git config --global user.email "your@xx.com" git config --global user.name "yourname" 初始化本地倉庫 首先,打開終端并切換到存放你代碼的項目目錄。接著執行以下命令,將該…

【后端高階面經:緩存篇】36、如何保證Redis分布式鎖的高可用和高性能?

一、分布式鎖核心挑戰:從單機到分布式的跨越 (一)分布式鎖的本質需求 互斥性:同一時刻僅一個客戶端持有鎖容錯性:節點故障時鎖仍有效(避免單點)原子性:加鎖/釋放鎖操作原子完成可重入性:支持同一客戶端多次獲取同一把鎖(二)Redis天然優勢 單線程模型保證操作原子性…

【后端高階面經:MongoDB篇】40、怎么優化MongoDB的查詢性能?

一、索引優化&#xff1a;構建高效查詢的基石 &#xff08;一&#xff09;索引類型與適用場景 1. 五大核心索引類型 索引類型適用場景示例代碼性能影響單字段索引單條件查詢&#xff08;如用戶ID、狀態字段&#xff09;db.users.createIndex({ user_id: 1 })低復合索引多條件…

Linux wget 常用命令詳解

目錄 1.1 工具定位 基礎下載示例 二、高效下載參數詳解 2.1 下載控制類 2.2 文件管理類 2.3 網絡優化類 三、高級應用場景 3.1 遞歸下載與整站鏡像 3.2 自動化下載實踐 3.3 安全下載配置 四、參數速查手冊 4.1 常用參數匯總 1.1 工具定位 基礎下載語法 wget [選項…

Pytorch中文文本分類

本文為&#x1f517;365天深度學習訓練營內部文章 原作者&#xff1a;K同學啊 將對中文文本進行分類&#xff0c;示例如下&#xff1a; 文本分類流程圖 1.加載數據 import time import pandas as pd import torch from torch.utils.data import DataLoader, random_split impo…

13.「極簡」扣子(coze)教程 | 小程序UI設計進階(三)讓界面動起來,實操講透“聚焦”事件

前一期大師兄介紹了扣子平臺組件的兩種狀態“禁用”和“加載”。這兩種方法使控件可以通過簡單設置表示出更多的運行狀態。今天大師兄將詳細介紹控件的一種事件“聚焦”。 扣子&#xff08;coze&#xff09;編程 「極簡」扣子(coze)教程 | 小程序UI設計進階 II&#xff01;讓…

劍指offer11_矩陣中的路徑

矩陣中的路徑 請設計一個函數&#xff0c;用來判斷在一個矩陣中是否存在一條路徑包含的字符按訪問順序連在一起恰好為給定字符串。 路徑可以從矩陣中的任意一個格子開始&#xff0c;每一步可以在矩陣中向左&#xff0c;向右&#xff0c;向上&#xff0c;向下移動一個格子。 如…

騰訊2025年校招筆試真題手撕(三)

一、題目 今天正在進行賽車車隊選拔&#xff0c;每一輛賽車都有一個不可以改變的速度。現在需要選取速度差距在10以內的車隊&#xff08;車隊中速度的最大值減去最小值不大于10&#xff09;&#xff0c;用于迎賓。車隊的選拔按照的是人越多越好的原則&#xff0c;給出n輛車的速…

《三維點如何映射到圖像像素?——相機投影模型詳解》

引言 以三維投影介紹大多比較分散&#xff0c;不少小伙伴再面對諸多的坐標系轉換中容易弄混&#xff0c;特別是再寫代碼的時候可能搞錯&#xff0c;所有這篇文章幫大家完整的梳理3D視覺中的投影變換的全流程&#xff0c;一文弄清楚這個過程&#xff0c;幫助大家搞清坐標系轉換…

Ini配置文件讀寫,增加備注功能

1.增加備注項寫入 例: #節點備注 [A] #項備注 bbb1 ccc2 [B] bbb1 IniConfig2 ic new IniConfig2(); //首次寫入 if (!ic.CanRead()) { ic.AddSectionReMarke("A", "節點備注"); ic.SetValue("A&qu…

OpenHarmony 5.0中狀態欄添加以太網狀態欄圖標以及功能實現

目錄 1.前置條件 2.方案 1.前置條件 首先以太網接口是有問題的,如下按照如下流程將以太網接口進行修復 OpenHarmony 以太網卡熱插拔事件接口無效-CSDN博客 然后上述的接口可以了就可以通過這個接口獲取以太網是否連接狀態 要注意wifi連接的干擾和預置虛擬網口干擾 2.方案…