C++設計模式之——享元模式詳解和代碼案例

文章目錄

    • C++中實現享元模式通常涉及以下幾個關鍵部分:
    • 一個簡單的C++代碼片段示例
    • 享元模式的進一步說明
    • C++享元模式代碼案例——咖啡店訂單系統
    • 享元模式在現實世界的應用場景

C++中實現享元模式通常涉及以下幾個關鍵部分:

享元模式(Flyweight Pattern)是一種用于性能優化的設計模式,它通過共享對象來有效支持大量的細粒度對象,以減少內存消耗。這種模式適用于那些對象中大部分狀態可以外部化,而只有少量內部狀態(不隨環境改變而改變的狀態)的對象。在C++中實現享元模式通常涉及以下幾個關鍵部分:

  1. 抽象享元(Flyweight Interface)
    定義了所有具體享元類共有的接口,通常包含內部狀態和外部狀態的操作方法。內部狀態是可以共享的,而外部狀態由客戶端在每次調用時傳入。
class IFlyweight {
public:virtual ~IFlyweight() {}// 內部狀態不變的操作virtual void operation(const Context& context) const = 0;
};
  1. 具體享元(Concrete Flyweight)
    實現了抽象享元接口,并存儲內部狀態。具體享元類的實例是可共享的。
class ConcreteFlyweight : public IFlyweight {
private:std::string intrinsicState; // 內部狀態
public:ConcreteFlyweight(const std::string& state) : intrinsicState(state) {}void operation(const Context& context) const override {// 使用內部狀態和外部狀態進行操作}
};
  1. 享元工廠(Flyweight Factory)
    負責創建和管理享元對象。它確保當請求的是相同的內部狀態時,不會創建多個具有相同內部狀態的享元對象。
class FlyweightFactory {
private:std::map<std::string, std::shared_ptr<IFlyweight>> flyweights;
public:std::shared_ptr<IFlyweight> getFlyweight(const std::string& key) {if (flyweights.find(key) == flyweights.end()) {flyweights[key] = std::make_shared<ConcreteFlyweight>(key);}return flyweights[key];}
};

一個簡單的C++代碼片段示例

下面是一個簡單的C++代碼片段示例,展示了如何使用享元模式:

#include <iostream>
#include <string>
#include <map>
#include <memory>// 抽象享元
class IFlyweight {
public:virtual ~IFlyweight() {}virtual void operation(const std::string& extrinsicState) const = 0;
};// 具體享元
class ConcreteFlyweight : public IFlyweight {
private:std::string intrinsicState;
public:ConcreteFlyweight(const std::string& state) : intrinsicState(state) {}void operation(const std::string& extrinsicState) const override {std::cout << "Concrete Flyweight: Internal State = " << intrinsicState<< ", Extrinsic State = " << extrinsicState << std::endl;}
};// 享元工廠
class FlyweightFactory {
private:std::map<std::string, std::shared_ptr<IFlyweight>> flyweights;
public:std::shared_ptr<IFlyweight> getFlyweight(const std::string& key) {if (flyweights.count(key) == 0) {flyweights[key] = std::make_shared<ConcreteFlyweight>(key);}return flyweights[key];}
};int main() {FlyweightFactory factory;std::vector<std::string> extrinsicStates = {"state1", "state2", "state2"};for (const auto& state : extrinsicStates) {auto flyweight = factory.getFlyweight("sharedState");flyweight->operation(state);}return 0;
}

在這個例子中,每當客戶端請求一個享元對象時,工廠會檢查是否已經存在具有相同內部狀態的對象。如果存在,則返回已有的對象;如果不存在,則創建新的具體享元對象并存儲起來供后續請求使用。這樣,多個帶有不同外部狀態的對象就可以共享同一個具有固定內部狀態的具體享元對象,從而節約內存。

讓我們深入探討一下上面代碼示例的實際意義和應用場景。在上述例子中,ConcreteFlyweight 類的內部狀態是字符串 "sharedState",它是可共享的。而外部狀態則是傳遞給 operation() 方法的參數 extrinsicState,每次調用時可能不同。

例如,假設我們正在創建一個文本渲染引擎,其中有許多字符需要繪制,但許多字符共享同一張圖片資源。在這種情況下,字符的形狀(字體樣式、顏色等)可以視為內部狀態,這部分是固定的且可以共享;而字符的位置、旋轉角度、縮放比例等則可以視為外部狀態,這些屬性會隨著字符在不同上下文中的使用而變化。

class CharacterFlyweight : public IFlyweight {
private:Texture texture; // 內部狀態,表示字符圖像紋理
public:CharacterFlyweight(const Texture& tex) : texture(tex) {}void operation(const RenderingContext& context) const override {// 使用共享的紋理資源,根據context中的位置、旋轉角度等繪制字符}
};class CharacterFactory {
private:std::map<CharacterStyle, std::shared_ptr<IFlyweight>> characters;
public:std::shared_ptr<IFlyweight> getCharacter(const CharacterStyle& style) {if (characters.find(style) == characters.end()) {Texture tex = loadTexture(style.getFontFile()); // 加載圖片資源characters[style] = std::make_shared<CharacterFlyweight>(tex);}return characters[style];}
};struct RenderingContext {Point position;double rotation;float scale;// 其他外部狀態...
};int main() {CharacterFactory factory;// 渲染不同位置的同一種字符CharacterStyle sharedStyle("Arial.ttf");RenderingContext ctx1{Point{10, 20}, 0.0, 1.0};RenderingContext ctx2{Point{30, 40}, 0.0, 1.0};auto character = factory.getCharacter(sharedStyle);character->operation(ctx1);character->operation(ctx2);return 0;
}

在這個例子中,CharacterFlyweight 類是具體的享元類,存儲了字符的紋理(內部狀態)。CharacterFactory 類作為享元工廠,負責創建和管理字符享元對象,保證相同的字符樣式只加載一次圖片資源。每次需要渲染字符時,客戶端獲取對應字符享元對象并傳入上下文(即外部狀態),從而有效地減少了重復加載資源造成的內存消耗。

享元模式的進一步說明

進一步說明,享元模式(Flyweight Pattern)的核心目標是通過共享技術有效支持大量細粒度的對象,從而節省系統資源。在上述文本渲染引擎的例子中:

  • CharacterFlyweight 是享元類,它封裝了字符的基本視覺表現——也就是共享的紋理資源(內部狀態),并且提供了 operation() 方法來執行實際的渲染操作,該方法接受 RenderingContext 對象,包含了外部狀態如位置、旋轉角度和縮放比例。

  • CharacterFactory 負責管理和創建享元對象,確保相同字符樣式只創建一個實例。當請求某個樣式的字符時,如果該樣式對應的享元對象尚不存在,則加載相應的紋理資源并創建新的 CharacterFlyweight 實例;若已經存在,則直接返回已有的實例。

  • RenderingContext 表示每個字符實例的特定環境或配置,這是外部狀態的具體體現,不被多個字符實例共享。每渲染一個字符時,都會根據當前的渲染上下文調整字符的表現形式。

在實際應用中,通過這樣的設計,即便文檔中有成千上萬個同類型的字符需要渲染,也只需要保存一份共享的紋理資源,大大降低了系統的內存占用。同時,由于外部狀態的變化不影響內部狀態的復用,使得系統能靈活應對各種不同的渲染需求。

除此之外,享元模式還有助于減少系統中對象的數量,進而降低系統運行時的內存消耗和CPU開銷。特別是在大型系統中,合理運用享元模式能夠顯著提升性能和響應速度。不過需要注意的是,不是所有的系統或場景都適用享元模式,應根據具體情況判斷是否滿足以下條件:

  1. 對象數量龐大且內部狀態大部分可以共享:如果系統中存在大量相似對象,且這些對象之間的大部分狀態是相同的,那么就可以考慮使用享元模式。

  2. 內部狀態較少且相對穩定:內部狀態是指那些不會隨著環境改變而改變的狀態,外部狀態則是與具體使用環境相關的狀態。享元對象應該盡量少地持有內部狀態,并通過參數傳遞外部狀態。

  3. 對象的創建成本高:如果對象的創建過程比較耗時或耗費資源,通過享元模式復用已有對象可以顯著減少這些成本。

總結起來,享元模式在游戲開發、圖形渲染、數據庫連接池、緩存系統等領域有廣泛應用。在實現時需權衡好內存消耗和程序邏輯復雜度的關系,確保模式的有效性和易維護性。

C++享元模式代碼案例——咖啡店訂單系統

以下是一個簡單的C++享元模式代碼案例,該案例模擬了一個咖啡店訂單系統,其中咖啡口味被視為享元對象,可以被多個訂單共享:

#include <iostream>
#include <map>
#include <memory>// 抽象享元接口
class CoffeeFlavor {
public:virtual ~CoffeeFlavor() {}virtual void serve() const = 0;
};// 具體享元類
class ConcreteCoffeeFlavor : public CoffeeFlavor {
public:explicit ConcreteCoffeeFlavor(const std::string& flavorName): flavorName_(flavorName) {}void serve() const override {std::cout << "Serving coffee flavor: " << flavorName_ << std::endl;}private:std::string flavorName_;
};// 享元工廠
class CoffeeFlavorFactory {
private:std::map<std::string, std::shared_ptr<CoffeeFlavor>> flavors;public:std::shared_ptr<CoffeeFlavor> getOrder(const std::string& flavor) {if (flavors.find(flavor) == flavors.end()) {flavors[flavor] = std::make_shared<ConcreteCoffeeFlavor>(flavor);}return flavors[flavor];}
};int main() {CoffeeFlavorFactory factory;// 創建幾個不同的訂單,但某些口味會被共享std::vector<std::string> orders = {"Espresso", "Latte", "Espresso", "Cappuccino", "Espresso"};for (const auto& order : orders) {auto flavor = factory.getOrder(order);flavor->serve();}return 0;
}

在這個例子中:

  • CoffeeFlavor 是抽象享元類,定義了所有咖啡口味的基本行為,即服務(serve)咖啡。
  • ConcreteCoffeeFlavor 是具體享元類,實現了咖啡口味的具體行為,并存儲了口味名稱這一內部狀態。
  • CoffeeFlavorFactory 是享元工廠,它維護了一個儲存所有咖啡口味實例的映射表。當客戶請求某個口味的咖啡時,工廠會檢查是否已經創建過該口味的實例,如果沒有則創建一個新的實例,否則返回已有的實例,從而實現了口味的共享。

運行此代碼,可以看到雖然訂單列表中有多個"Espresso",但在打印結果中只會看到一次"Serving coffee flavor: Espresso",這是因為享元模式讓多次請求相同口味的咖啡共享了同一個實例。

實際上,上述咖啡口味享元模式的案例并沒有體現出享元模式對外部狀態的處理。在一個更全面的示例中,我們可能還會遇到咖啡訂單具有外部狀態,如客戶的偏好(加糖、加奶)、杯子大小等。這時,我們可以對示例稍作修改,將外部狀態從享元對象中分離出來:

#include <iostream>
#include <map>
#include <memory>// 抽象享元接口
class CoffeeFlavor {
public:virtual ~CoffeeFlavor() {}virtual void serve(const std::string& extras, const std::string& size) const = 0;
};// 具體享元類
class ConcreteCoffeeFlavor : public CoffeeFlavor {
public:explicit ConcreteCoffeeFlavor(const std::string& flavorName): flavorName_(flavorName) {}void serve(const std::string& extras, const std::string& size) const override {std::cout << "Serving " << size << " cup of " << flavorName_<< " with extras: " << extras << std::endl;}private:std::string flavorName_;
};// 享元工廠
class CoffeeFlavorFactory {
private:std::map<std::string, std::shared_ptr<CoffeeFlavor>> flavors;public:std::shared_ptr<CoffeeFlavor> getOrder(const std::string& flavor) {if (flavors.find(flavor) == flavors.end()) {flavors[flavor] = std::make_shared<ConcreteCoffeeFlavor>(flavor);}return flavors[flavor];}
};// 訂單類,包含外部狀態
class CoffeeOrder {
public:CoffeeOrder(std::shared_ptr<CoffeeFlavor> flavor, const std::string& extras, const std::string& size): flavor_(flavor), extras_(extras), size_(size) {}void serve() const {flavor_->serve(extras_, size_);}private:std::shared_ptr<CoffeeFlavor> flavor_;std::string extras_;std::string size_;
};int main() {CoffeeFlavorFactory factory;// 創建幾個不同的訂單,但某些口味會被共享std::vector<CoffeeOrder> orders = {{factory.getOrder("Espresso"), "no sugar", "small"},{factory.getOrder("Latte"), "extra foam", "medium"},{factory.getOrder("Espresso"), "double sugar", "large"},{factory.getOrder("Cappuccino"), "cinnamon", "medium"},{factory.getOrder("Espresso"), "single sugar", "small"}};for (const auto& order : orders) {order.serve();}return 0;
}

在這個修改后的示例中,我們創建了一個CoffeeOrder類,它包含了外部狀態(額外配料和杯子大小),并在serve()方法中將這些外部狀態傳遞給享元對象。即使多個訂單選擇了相同的咖啡口味,由于外部狀態的不同,每個訂單都能得到個性化的服務。

享元模式在現實世界的應用場景

討論享元模式在現實世界的應用場景,除了前面提到的咖啡訂單系統以外,還有很多其他例子可以借鑒:

  1. 字體渲染:在圖形用戶界面或者文字處理軟件中,同一字體的不同實例可以共享字體文件數據,字體的尺寸、顏色、陰影等效果可以作為外部狀態傳遞給字體享元對象。

  2. 圖形渲染:在游戲開發中,大量的小顆粒物(如草地上的草葉、森林里的樹葉等)可以共享同樣的紋理和模型數據,而位置、旋轉角度、縮放比例等作為外部狀態傳遞。

  3. 數據庫連接池:在Web服務器中,數據庫連接是非常寶貴的資源,通過享元模式可以復用已建立的數據庫連接,避免頻繁創建和銷毀連接帶來的性能損耗。這里的連接對象就是享元對象,連接參數(數據庫地址、用戶名、密碼等)則是外部狀態。

  4. HTTP 請求緩存:在Web服務中,針對相同的URL發起的GET請求可以復用之前請求的結果,而不是每次都重新發送請求。這里HTTP請求結果可以看作享元對象,請求參數和URL作為外部狀態。

在上述各個場景中,享元模式通過共享內部狀態(那些不隨環境變化而變化的部分)的實例,有效地節省了系統資源,提升了整體性能。同時,它通過分離內部狀態和外部狀態,使得系統能夠靈活地應對各種復雜的業務場景。
python推薦學習匯總連接:
50個開發必備的Python經典腳本(1-10)

50個開發必備的Python經典腳本(11-20)

50個開發必備的Python經典腳本(21-30)

50個開發必備的Python經典腳本(31-40)

50個開發必備的Python經典腳本(41-50)
————————————————

?最后我們放松一下眼睛
在這里插入圖片描述

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

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

相關文章

LCR 153. 二叉樹中和為目標值的路徑

解題思路&#xff1a; 回溯&#xff1a;先序遍歷&#xff0b;路徑記錄 class Solution {LinkedList<List<Integer>> res new LinkedList<>();LinkedList<Integer> path new LinkedList<>();public List<List<Integer>> pathTarge…

android 如何動態修改swap

前言 當前項目中發現&#xff0c;產品在長時間使用后&#xff0c;會概率死機&#xff0c;通過log分析&#xff0c;可能和swap 大小太小導致的&#xff0c;需要修改增大swap大小后&#xff0c;壓測驗證。如何查看swap大小 cat /proc/swaps C:\Users\Administrator>adb shel…

元學習(meta-learning)的通俗解釋

目錄 1、什么是元學習 2、元學習還可以做什么 3、元學習是如何訓練的 1、什么是元學習 meta-learning 的一個很經典的英文解釋是 learn to learn&#xff0c;即學會學習。元學習是一個很寬泛的概念&#xff0c;可以有很多實現的方式&#xff0c;下面以目標檢測的例子來解釋…

阿里Replace Anything:一鍵替換萬物,讓圖像編輯更簡單

最近&#xff0c;阿里巴巴智能研究院在AIGC領域可謂動作頻頻&#xff0c;新品發布不斷&#xff0c;在之前的文章已經向大家介紹了關于Animate AnyOne, Outfit Anyone&#xff0c;AnyText, AnyDoor等相關技術&#xff0c;感興趣的小伙伴可以點擊下面鏈接閱讀&#xff5e; AI一鍵…

Laravel - API 項目適用的圖片驗證碼

1. 安裝 gregwar/captcha 圖片驗證碼接口的流程是&#xff1a; 生成圖片驗證碼 生成隨機的 key&#xff0c;將驗證碼文本存入緩存。 返回隨機的 key&#xff0c;以及驗證碼圖片 # 不限于 laravel 普通 php 項目也可以使用額 $ composer require gregwar/captcha2. 開發接口 …

神經網絡算法詳解以及應用場景

神經網絡算法是一類基于神經網絡思想的機器學習算法。神經網絡是一種模擬人腦神經系統的計算模型&#xff0c;由大量的人工神經元組成&#xff0c;這些神經元通過可調的連接權值相互連接&#xff0c;形成復雜的網絡結構。神經網絡具有大規模并行處理、分布式信息存儲、良好的自…

小塔RFID技術幫您解決“倉儲管理危機”!

商品積壓對一個企業帶來的影響是久遠的&#xff0c;倉儲管理流轉失衡&#xff1a;庫存數據不準確、繁瑣人工管理費時費力、商品爆倉及庫存短缺等造成“倉儲管理危機”&#xff0c;讓企業自身陷入困境。 優化倉儲管理&#xff0c;小塔RFID倉儲管理方案輕松解決。利用RFID&#x…

java數據結構與算法刷題-----LeetCode538. 把二叉搜索樹轉換為累加樹

java數據結構與算法刷題目錄&#xff08;劍指Offer、LeetCode、ACM&#xff09;-----主目錄-----持續更新(進不去說明我沒寫完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目錄 解題思路 BST二叉搜索樹&#xff0c;中序遍歷結果為一個升序序列…

【C語言】三子棋

前言&#xff1a; 三子棋是一種民間傳統游戲&#xff0c;又叫九宮棋、圈圈叉叉棋、一條龍、井字棋等。游戲規則是雙方對戰&#xff0c;雙方依次在9宮格棋盤上擺放棋子&#xff0c;率先將自己的三個棋子走成一條線就視為勝利。但因棋盤太小&#xff0c;三子棋在很多時候會出現和…

心得 醒悟

學習是一個學習&#xff0c;遺忘&#xff0c;回憶。。。的一個不間斷個過程&#xff0c;學習python很心急&#xff0c;沒有怎么實操&#xff0c;開著2倍速看視頻&#xff0c;導致看過后很多對列表&#xff0c;字典的命令等等就忘記了&#xff0c;沒有真的學會&#xff0c;導致現…

Unity(第十四部)光照

原始的有默認燈光、除了默認的你還可以創建 1、定向光源&#xff08;類似太陽、從無限遠的地方射向地面的光&#xff0c;光源位置并不影響照射角度等&#xff0c;不同方向的旋轉影響角度和明亮&#xff09; 1. 顏色&#xff1a;調整光的顏色2. 模式&#xff1a;混合是實時加烘…

FCU2601嵌入式控制單元獲得開普「電磁兼容檢驗證書」

近日&#xff0c;飛凌嵌入式專為鋰電池儲能行業設計的FCU2601嵌入式控制單元獲得了開普電磁兼容檢驗證書&#xff0c;此次性能檢驗項目包括高頻干擾檢驗、靜電放電干擾檢驗、輻射電磁場干擾檢驗、快速瞬變脈沖群干擾檢驗、浪涌干擾檢驗、工頻磁場干擾檢驗、阻尼振蕩磁場干擾檢驗…

基于docker實現MySQL主從復制(全網最詳細!!!)

一、 通過docker鏡像搭建MySQL主從 主服務器&#xff1a;容器名zi-mysql-master&#xff0c;端口3306 從服務器&#xff1a;容器名zi-mysql-slave1&#xff0c;端口3307 從服務器&#xff1a;容器名zi-mysql-slave2&#xff0c;端口3308 二、 關閉防火墻&#xff0c;啟動docker…

免費百度快速收錄軟件

在網站SEO的過程中&#xff0c;不斷更新網站內容是提升排名和吸引流量的關鍵之一。而對于大多數網站管理員來說&#xff0c;頻繁手動更新文章并進行SEO優化可能會是一項繁瑣且耗時的任務。針對這一問題&#xff0c;百度自動更新文章SEO工具應運而生&#xff0c;它能夠幫助網站管…

基于R語言APSIM模型進階應用與參數優化、批量模擬教程

原文&#xff1a;基于R語言APSIM模型進階應用與參數優化、批量模擬教程 前沿 隨著數字農業和智慧農業的發展&#xff0c;基于過程的農業生產系統模型在模擬作物對氣候變化的響應與適應、農田管理優化、作物品種和株型篩選、農田固碳和溫室氣體排放等領域扮演著越來越重要的作…

全域營銷、全員營銷與霸詞、霸屏、霸網:揭秘數字營銷的五大核心策略

全域營銷、全員營銷與霸詞、霸屏、霸網&#xff1a;揭秘數字營銷的五大核心策略 隨著科技的飛速發展和互聯網的深度滲透&#xff0c;數字營銷已成為企業品牌推廣和市場拓展的必備手段。全域營銷、全員營銷、霸詞、霸屏和霸網這五大策略&#xff0c;更是引領著數字營銷的新潮流…

CCF-A類 IEEE VIS‘24 3月31日截稿!探索可視化技術的無限可能!

會議之眼 快訊 IEEE VIS (IEEE Visualization Conference )即可視化大會將于 2024 年 10月13日 -18日在美國佛羅里達州皮特海灘的信風島大海灘度假舉行&#xff01;圣彼得海灘&#xff0c;以其迷人的日落和和煦的微風&#xff0c;作為激發創造力和促進可視化社區內合作的完美背…

工廠模式 詳解 設計模式

工廠模式 其主要目的是封裝對象的創建過程&#xff0c;使客戶端代碼和具體的對象實現解耦。這樣子就不用每次都new對象&#xff0c;更換對象的話&#xff0c;所有new對象的地方也要修改&#xff0c;違背了開閉原則&#xff08;對擴展開放&#xff0c;對修改關閉&#xff09;。…

使用Gson解析數組類型的JSON字符串

要使用Gson解析數組類型的JSON字符串&#xff0c;首先需要創建一個Java類來表示這個數組中的數據類型。例如&#xff0c;假設我們要解析的JSON數組如下&#xff1a; [{"name": "John Doe","age": 35},{"name": "Jane Smith"…

win中刪除不掉的文件,火絨粉碎刪除親測有效

看網上的 win R 然后終端輸入什么刪除的&#xff0c;照做了都沒有刪掉 有火絨的可以試試&#xff1a; 拖進去就刪掉了 很好使