軟體設計模式系列之十三——享元模式

来源:https://www.cnblogs.com/coodream2009/archive/2023/09/23/17724939.html
-Advertisement-
Play Games

Uber公司技術棧介紹 Uber(Uber Technologies,Inc.)中文譯作“優步”,是一家美國矽谷的科技公司。Uber在2009年,由加利福尼亞大學洛杉磯分校輟學生特拉維斯·卡蘭尼克和好友加勒特·坎普(Garrett Camp)創立。因旗下同名打車APP而名聲大噪。Uber已經進入中國 ...


1 模式的定義

享元模式(Flyweight Pattern)是一種結構型設計模式,它旨在減少記憶體占用或計算開銷,通過共用大量細粒度對象來提高系統的性能。這種模式適用於存在大量相似對象實例,但它們的狀態可以外部化(extrinsic),並且可以在多個對象之間共用的情況。

2 舉例說明

為了更好地理解享元模式,讓我們舉一些現實生活中的例子。

咖啡店的咖啡杯和碟子的例子。在咖啡店中,咖啡杯和碟子通常具有相同的設計和形狀,但它們可能具有不同的顏色或圖案。咖啡店可以使用享元模式來共用相同設計的杯子和碟子,以減少存儲和管理的成本。

公共交通卡的例子。城市中的公共交通卡(如地鐵卡、公車卡)通常具有相同的功能和外觀,但每張卡可能包含不同的餘額和個人信息。這些卡可以被視為享元對象,公共交通系統可以共用卡的通用功能。

電子書閱讀器的字體和樣式的例子。電子書閱讀器可以使用享元模式來管理字體、字型大小和樣式。多本電子書可以共用相同的字體和樣式設置,以提供一致的閱讀體驗。

這些例子都涉及到具有相似屬性和功能的對象,它們可以通過享元模式來共用通用部分,從而減少資源消耗並提高效率。這在設計和生產中可以節省時間和成本。

3 結構

享元模式的結構包括以下主要組件:

享元工廠(Flyweight Factory):享元工廠負責創建和管理享元對象。它維護一個享元池,其中包含已經創建的享元對象,並根據客戶端請求共用已經存在的對象或創建新的享元對象。

享元介面(Flyweight Interface):享元介面是享元對象的抽象,通常聲明瞭享元對象的公共方法,以便客戶端能夠訪問和操作享元對象。

具體享元(Concrete Flyweight):具體享元是享元介面的實現,包含了內部狀態和外部狀態。內部狀態是可以被共用的,而外部狀態是不可共用的,它在運行時傳遞給享元對象。

客戶端(Client):客戶端是使用享元模式的應用程式或模塊,它通過享元工廠來獲取或共用享元對象,並根據需要傳遞外部狀態。

4 實現步驟

要實現享元模式,可以按照以下步驟進行操作:

確定內部狀態和外部狀態:首先,確定對象的內部狀態和外部狀態。內部狀態是可以被多個對象共用的部分,而外部狀態是不可共用的。

創建享元介面:定義享元介面,聲明享元對象的公共方法,包括操作內部狀態和外部狀態的方法。

創建具體享元類:實現具體享元類,它包含了內部狀態和外部狀態的具體實現。內部狀態可以在多個對象之間共用,而外部狀態需要在運行時傳遞。

創建享元工廠:創建享元工廠,負責創建和管理享元對象。享元工廠可以維護一個享元池,用於存儲已經創建的享元對象。

客戶端使用享元對象:在客戶端中,通過享元工廠來獲取或共用享元對象。客戶端需要提供外部狀態作為參數,並根據需要操作享元對象。

5 代碼實現

以下是一個簡單的 Java 代碼示例,演示瞭如何使用享元模式來實現公共交通卡的共用功能。在這個示例中,我們創建了一個 TransportCardFactory 工廠類來管理交通卡對象,以及一個 TransportCard 介面表示交通卡。

// 1. 定義交通卡介面
interface TransportCard {
    void swipe();
}

// 2. 創建具體的交通卡類
class SubwayCard implements TransportCard {
    private String ownerName;
    private int balance;

    public SubwayCard(String ownerName) {
        this.ownerName = ownerName;
        this.balance = 0;
    }

    public void swipe() {
        System.out.println("刷地鐵卡,扣除票價,餘額:" + balance);
    }
}

// 3. 創建享元工廠類
class TransportCardFactory {
    private Map<String, TransportCard> cards = new HashMap<>();

    public TransportCard getCard(String ownerName) {
        if (cards.containsKey(ownerName)) {
            System.out.println("使用現有的交通卡:" + ownerName);
            return cards.get(ownerName);
        } else {
            System.out.println("創建新的交通卡:" + ownerName);
            TransportCard card = new SubwayCard(ownerName);
            cards.put(ownerName, card);
            return card;
        }
    }
}

// 4. 客戶端代碼
public class Client {
    public static void main(String[] args) {
        TransportCardFactory cardFactory = new TransportCardFactory();

        // 乘客1刷卡
        TransportCard card1 = cardFactory.getCard("zhanngsan");
        card1.swipe();

        // 乘客2刷卡
        TransportCard card2 = cardFactory.getCard("lisi");
        card2.swipe();

        // 再次刷卡
        TransportCard card3 = cardFactory.getCard("zhanngsan");
        card3.swipe();
    }
}

在這個示例中,我們首先定義了 TransportCard 介面,表示交通卡的通用功能。然後,我們創建了一個具體的交通卡類 SubwayCard,它實現了 TransportCard 介面,並包含了特定於地鐵卡的屬性。

接下來,我們創建了享元工廠類 TransportCardFactory,它負責管理和共用交通卡對象。當客戶端需要一個交通卡時,工廠類會首先檢查是否已經存在具有相同擁有者姓名的卡,如果存在則返回現有的卡,否則創建一個新的卡對象。

最後,我們在客戶端代碼中演示瞭如何使用享元模式,創建並刷卡,觀察到當兩位乘客使用相同姓名刷卡時,會共用同一個交通卡對象,從而減少了卡對象的創建和記憶體占用。

6 典型應用場景

6.1 享元模式通常在以下情況下得到廣泛應用

  • 大量對象。當系統中存在大量相似對象實例時,使用享元模式可以顯著減少記憶體占用,因為相似對象的內部狀態可以共用。

  • 內部狀態與外部狀態。當對象可以分為內部狀態和外部狀態時,享元模式特別有用。內部狀態是對象的固定部分,可以被多個對象共用,而外部狀態是對象的可變部分,每個對象可以根據需要個性化。

  • 性能優化。在需要高性能和低記憶體消耗的情況下,享元模式可以用於共用重覆使用的對象,從而提高系統的性能。

  • 緩存管理。在需要緩存大量對象以提高系統響應時間的情況下,可以使用享元模式來管理緩存對象。

  • 資源池管理。當需要管理共用資源池(如資料庫連接池、線程池)中的資源對象時,享元模式可以用於有效地共用和重用資源。

享元模式在需要管理大量相似對象、共用內部狀態、提高性能和減少記憶體占用的情況下非常有用。它允許對象在不同上下文中共用內部狀態,而外部狀態可以根據需要進行個性化定製。通過合理使用享元模式,可以改善系統的效率和資源利用率。

6.2 java中的字元串應用享元模式場景

在Java中,字元串是使用享元模式的經典示例。享元模式的核心思想是共用相似對象的內部狀態,以減少記憶體占用。字元串的使用正是基於這個思想。

下麵是Java中字元串如何使用享元模式的一些關鍵特點:

不可變性:Java中的字元串是不可變的,也就是說一旦創建了一個字元串對象,它的值就不能被修改。這意味著如果兩個字元串具有相同的字元序列,它們可以共用相同的內部字元數組。

字元串常量池:Java維護了一個字元串常量池(String Pool),用於存儲字元串字面量。當你創建一個字元串字面量時,Java會首先檢查常量池中是否已經存在相同值的字元串。如果存在,它將返回常量池中的字元串引用,而不會創建新的對象。

共用相同的字元串對象:由於字元串的不可變性和字元串常量池的存在,多個字元串變數可以共用相同的字元串對象。這意味著如果你有多個字元串變數引用相同的字元串值,它們實際上共用同一個字元串對象。

下麵是一個示例,演示了字元串如何使用享元模式:

String s1 = "Hello"; // 創建一個字元串字面量,存儲在常量池中
String s2 = "Hello"; // 與s1共用相同的字元串對象

String s3 = new String("Hello"); // 創建一個新的字元串對象,不存儲在常量池中
String s4 = new String("Hello"); // 創建另一個新的字元串對象,也不存儲在常量池中

System.out.println(s1 == s2); // true,s1和s2共用相同的字元串對象
System.out.println(s1 == s3); // false,s1和s3引用不同的字元串對象

在上面的示例中,s1 和 s2 共用相同的字元串對象,因為它們引用相同的字元串字面量,而 s3 和 s4 創建了新的字元串對象,因為它們使用了 new 操作符。這種共用內部狀態的方式減少了記憶體占用,並提高了性能,特別是當處理大量字元串時。

Java中的字元串是一個典型的享元模式的例子,通過不可變性和字元串常量池,它實現了字元串對象的共用,以減少記憶體占用和提高性能。這種設計對於處理字元串操作非常高效,並且保證了字元串值的安全性,因為它們不可被修改。

7 優缺點

享元模式具有一些優點和缺點,讓我們來看看:

優點:

減少記憶體占用,享元模式通過共用相似對象的內部狀態,可以大大減少記憶體占用,提高系統的性能和效率。提高性能,通過共用對象,減少了對象的創建和銷毀,從而提高了系統的性能。分離內部狀態和外部狀態,享元模式允許將內部狀態和外部狀態分開,外部狀態可以在運行時傳遞給享元對象,使系統更靈活。

缺點:

增加複雜性,享元模式引入了共用對象和外部狀態的概念,可能增加了系統的複雜性。可能導致線程安全問題,如果多個線程同時訪問共用對象並修改其外部狀態,可能會導致線程安全問題。

8 類似模式

享元模式通常與其他設計模式一起使用,以解決更複雜的問題或實現更全面的系統。以下是一些常見的設計模式,以及如何與享元模式一起使用它們。

工廠模式。享元模式通常需要一個工廠來創建和管理共用對象。你可以使用工廠模式來創建享元對象,確保對象的創建和初始化過程是封裝的,並且客戶端不需要直接創建對象。

單例模式。在享元模式中,享元工廠可以是一個單例,以確保只有一個享元工廠實例用於管理共用對象。這樣可以確保對象的唯一性和一致性。

裝飾者模式:裝飾者模式可以與享元模式一起使用,以動態地添加功能或狀態到享元對象。裝飾者模式允許你在不改變對象結構的情況下,為對象添加額外的行為。

代理模式:代理模式可以與享元模式一起使用,以提供對享元對象的訪問控制或延遲載入。代理可以用於監控或限制對共用對象的訪問。

組合模式:組合模式用於將對象組織成樹形結構,享元模式可以用於共用組合中的相似對象,以減少記憶體占用。這在圖形和圖像處理應用中特別有用。

享元模式可以與許多其他設計模式一起使用,具體取決於系統的需求。它通常與創建型模式(如工廠模式、單例模式)和結構型模式(如裝飾者模式、代理模式)結合使用,以實現更靈活、高效和可維護的系統。在實際應用中,將多種模式結合使用可以更好地滿足複雜系統的需求。

9 小結

享元模式是一種有助於減少記憶體占用和提高系統性能的結構型設計模式。通過共用大量細粒度的對象,它可以有效地降低系統的資源消耗,特別適用於存在大量相似對象的場景。在設計和開發中,當需要創建大量相似對象時,可以考慮使用享元模式以提高系統的效率和性能。這種模式的核心思想是將對象的內部狀態與外部狀態分離,從而實現對象的共用和復用。


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

-Advertisement-
Play Games
更多相關文章
  • 9 月 19 日,Next.js 13.5 正式發佈,該版本通過以下方式提高了本地開發性能和可靠性: 本地伺服器啟動速度提高 22%:使用App和Pages Router可以更快地進行迭代 HMR(快速刷新)速度提高 29%:在保存更改時進行更快的迭代 記憶體使用量減少 40%:在運行next sta ...
  • 第一個vue-cli程式的準備工作 什麼是vue-cli? vue-cli是官方提供的一個腳手架工具,用於快速生成一個vue項目模板。 預先定義好的目錄結構和代碼,就好比咱們在創建maven項目時可以選擇創建一個骨架項目,這個骨架項目就是腳手架,有利於我們更加快速的開發。 環境準備(所涉及到的命令都 ...
  • 背景 在做管理台項目時,我們會經常使用到表單+表格+彈窗表單的組合,以完成對數據的增、刪、查、改。 在vue2+elementui項目中,使用彈窗dialog+表單form,實現對數據的添加和修改。 每次關閉彈窗時,使用resetFields方法對錶單進行重置。 下一次打開彈窗時, 如果是添加數據, ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 問題描述 最近遇到一個有意思的問題,項目中有一個地方,點擊需要跳轉到一個新的功能變數名稱地址 筆者使用a標簽做跳轉,跳是跳過去了,可是跳過去以後,反而打不開了,顯示403佛伯樂 蛤? 大致這樣的代碼: <a href="http://abcdef ...
  • 本文來盤點微軟開源的十大前端項目,這些項目在 Github 上獲得了超過 45 萬 Star! Visual Studio Code Visual Studio Code 是一款由微軟開發的開源的代碼編輯器。它支持多種編程語言,如C、C++、C#、Python、JavaScript 和 TypeSc ...
  • 1.原始值和引用值 ECMScript變數包含兩種不同類型是數據:原始值和引用值。 原始值:最簡單的數據。有6中原始值:Undefined、Null、Boolean、Number、String和Symbol。原始值是按值訪問。 引用值:由多個值構成的對象。三大引用類型:1.object 2.Arra ...
  • 1、案例:猜數字 設置一個1-10之間的隨機數,然後輸入進行猜數字,猜的大了怎麼樣、猜的小了怎麼樣、猜對了怎麼樣 知識點:設置隨機數 、if判斷 、while迴圈 寫題思路: 1.設置彈框提出問題 2.定義一個隨機數0-10的數組 3.if 判斷 取值的範圍,在其範圍內反饋的結果 4.while迴圈 ...
  • 搭建後臺管理系統模板 2.1項目初始化 今天來帶大家從0開始搭建一個vue3版本的後臺管理系統。一個項目要有統一的規範,需要使用eslint+stylelint+prettier來對我們的代碼質量做檢測和修複,需要使用husky來做commit攔截,需要使用commitlint來統一提交規範,需要使 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 微服務架構已經成為搭建高效、可擴展系統的關鍵技術之一,然而,現有許多微服務框架往往過於複雜,使得我們普通開發者難以快速上手並體驗到微服務帶了的便利。為瞭解決這一問題,於是作者精心打造了一款最接地氣的 .NET 微服務框架,幫助我們輕鬆構建和管理微服務應用。 本框架不僅支持 Consul 服務註 ...
  • 先看一下效果吧: 如果不會寫動畫或者懶得寫動畫,就直接交給Blend來做吧; 其實Blend操作起來很簡單,有點類似於在操作PS,我們只需要設置關鍵幀,滑鼠點來點去就可以了,Blend會自動幫我們生成我們想要的動畫效果. 第一步:要創建一個空的WPF項目 第二步:右鍵我們的項目,在最下方有一個,在B ...
  • Prism:框架介紹與安裝 什麼是Prism? Prism是一個用於在 WPF、Xamarin Form、Uno 平臺和 WinUI 中構建鬆散耦合、可維護和可測試的 XAML 應用程式框架 Github https://github.com/PrismLibrary/Prism NuGet htt ...
  • 在WPF中,屏幕上的所有內容,都是通過畫筆(Brush)畫上去的。如按鈕的背景色,邊框,文本框的前景和形狀填充。藉助畫筆,可以繪製頁面上的所有UI對象。不同畫筆具有不同類型的輸出( 如:某些畫筆使用純色繪製區域,其他畫筆使用漸變、圖案、圖像或繪圖)。 ...
  • 前言 嗨,大家好!推薦一個基於 .NET 8 的高併發微服務電商系統,涵蓋了商品、訂單、會員、服務、財務等50多種實用功能。 項目不僅使用了 .NET 8 的最新特性,還集成了AutoFac、DotLiquid、HangFire、Nlog、Jwt、LayUIAdmin、SqlSugar、MySQL、 ...
  • 本文主要介紹攝像頭(相機)如何採集數據,用於類似攝像頭本地顯示軟體,以及流媒體數據傳輸場景如傳屏、視訊會議等。 攝像頭採集有多種方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),網上一些文章以及 ...
  • 前言 Seal-Report 是一款.NET 開源報表工具,擁有 1.4K Star。它提供了一個完整的框架,使用 C# 編寫,最新的版本採用的是 .NET 8.0 。 它能夠高效地從各種資料庫或 NoSQL 數據源生成日常報表,並支持執行複雜的報表任務。 其簡單易用的安裝過程和直觀的設計界面,我們 ...
  • 背景需求: 系統需要對接到XXX官方的API,但因此官方對接以及管理都十分嚴格。而本人部門的系統中包含諸多子系統,系統間為了穩定,程式間多數固定Token+特殊驗證進行調用,且後期還要提供給其他兄弟部門系統共同調用。 原則上:每套系統都必須單獨接入到官方,但官方的接入複雜,還要官方指定機構認證的證書 ...
  • 本文介紹下電腦設備關機的情況下如何通過網路喚醒設備,之前電源S狀態 電腦Power電源狀態- 唐宋元明清2188 - 博客園 (cnblogs.com) 有介紹過遠程喚醒設備,後面這倆天瞭解多了點所以單獨加個隨筆 設備關機的情況下,使用網路喚醒的前提條件: 1. 被喚醒設備需要支持這WakeOnL ...
  • 前言 大家好,推薦一個.NET 8.0 為核心,結合前端 Vue 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...