首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 設計模式:享元模式 以共享物件解決大量細粒度物件記憶體壓力

     編輯:關於C語言

問題背景

文書編輯器需顯示數萬字元、遊戲需渲染成千上萬樹木/子彈、報表系統產生海量格儲存格物件——若每個字元、樹木、格子都 new 一個獨立物件,記憶體將迅速耗盡。享元模式透過共享內部狀態、外部化可變狀態,以極少物件支撐龐大邏輯數量。

核心概念

術語說明範例(文書編輯器)
內部狀態可共享、不隨環境改變字元碼 'A'、字型家族
外部狀態隨環境改變、不可共享字元位置、字號、顏色、粗體
享元工廠管理共享池、按鍵值取得/建立享元字元碼 → CharacterFlyweight

類別結構

享元模式結構圖

完整實作:文字編輯器字元共享

1. 抽象享元介面

public abstract class CharacterFlyweight
{
    // 外部狀態由呼叫端傳入
    public abstract void Display(int position, string font, int size, ConsoleColor color);
}

2. 具體享元(共享物件)

public class ConcreteCharacter : CharacterFlyweight
{
    private readonly char _symbol; // 內部狀態:唯讀、可共享

    public ConcreteCharacter(char symbol) => _symbol = symbol;

    public override void Display(int position, string font, int size, ConsoleColor color)
    {
        Console.ForegroundColor = color;
        Console.WriteLine($"字元:'{_symbol}' 位置:{position} 字型:{font} 大小:{size} 顏色:{color}");
        Console.ResetColor();
    }
}

3. 享元工廠(共享池管理)

public class CharacterFactory
{
    private readonly Dictionary _pool = new();

    public CharacterFlyweight GetCharacter(char key)
    {
        if (!_pool.TryGetValue(key, out var flyweight))
        {
            flyweight = new ConcreteCharacter(key);
            _pool[key] = flyweight;
            Console.WriteLine($"[Factory] 建立新享元: '{key}',池大小: {_pool.Count}");
        }
        return flyweight;
    }

    public int PoolSize => _pool.Count;
}

4. 客戶端使用

class Program
{
    static void Main()
    {
        var factory = new CharacterFactory();
        string text = "HELLO WORLD"; // 模擬文檔內容
        int pos = 0;

        foreach (char c in text)
        {
            if (c == ' ') { pos++; continue; } // 空白略過

            // 取得共享字元物件
            var flyweight = factory.GetCharacter(char.ToUpper(c));

            // 外部狀態:位置、字型、大小、顏色(這裡隨機示意)
            flyweight.Display(pos++, "Consolas", 12, (ConsoleColor)(pos % 16));
        }

        Console.WriteLine($"\n文檔長度: {text.Length},實際物件數: {factory.PoolSize}");
        Console.ReadKey();
    }
}

執行結果示意

[Factory] 建立新享元: 'H',池大小: 1
[Factory] 建立新享元: 'E',池大小: 2
[Factory] 建立新享元: 'L',池大小: 3
[Factory] 建立新享元: 'O',池大小: 4
[Factory] 建立新享元: 'W',池大小: 5
[Factory] 建立新享元: 'R',池大小: 6
[Factory] 建立新享元: 'D',池大小: 7

文檔長度: 11,實際物件數: 7

「HELLO WORLD」含 10 個非空白字元,但只有 7 個不重複字元,故僅建立 7 個物件即可服務所有顯示需求。

優缺點與適用場景

優點缺點
✅ 大幅減少物件數量,降低記憶體占用
✅ 內部狀態集中管理,易於維護
⚠️ 增加系統複雜度(需區分內/外部狀態)
⚠️ 執行時需查找/傳遞外部狀態,微增 CPU 開銷
⚠️ 共享池本身佔用記憶體,物件數極少時得不償失

✅ 適用場景(需同時滿足)

  • 系統中存在大量相似物件
  • 物件佔用大量記憶體
  • 大部分狀態可外部化
  • 去除外部狀態後,可按內部狀態分組,每組僅需一個共享物件
  • 系統不依賴物件身分識別(即 a == b 不重要)

實務應用舉例

  • 字元/圖示共享:文字編輯器、IDE 語法高亮、遊戲 UI 圖示
  • 資料庫連線池 / 執行緒池:連線物件為享元,連線參數為外部狀態
  • 圖形繪製:樹木、草地、子彈等大量重複 Sprite
  • String.Intern:.NET 執行期字串駐留池即為享元實作

結語

享元模式以「以少勝多」之勢,將不變的本質(內部狀態)共享,將變化的場景(外部狀態)外部化。在大量細粒度物件場景下,它是記憶體優化的利器;但切記:過早優化是萬惡之源,僅在記憶體分析確認瓶頸時再引入。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved