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

.NET 設計模式精解(十三):享元模式——共享細粒度物件,極致優化記憶體開銷

     編輯:關於.NET

面向物件雖解決抽象問題,但大量細粒度物件會導致執行期記憶體壓力。享元模式透過共享技術支援大量細粒度物件,本文以字元處理為例,展示從朴素設計到享元模式的重構演進,深入剖析內部/外部狀態分離、享元工廠、單例工廠等關鍵實作細節。

一、模式意圖與結構

意圖:運用共享技術有效支援大量細粒度物件。(GoF)

結構圖:

享元模式結構圖

二、生活實例:公共電話網資源共享

撥號音發生器、鈴聲發生器、接收器為所有用戶共享,用戶無感知即享用資源:

撥號音發生器享元示意

三、漸進式重構:字元處理系統

階段 1:朴素設計 (每字元一物件)


public abstract class Charactor {
    protected char _symbol; protected int _width, _height, _ascent, _descent, _pointSize;
    public abstract void Display();
}
public class CharactorA : Charactor { /* 固定屬性 */ }
public class CharactorB : Charactor { ... }
public class CharactorC : Charactor { ... }

文檔中上萬字元 → 上萬物件 → 記憶體耗盡。

階段 2:共享不可變狀態 (移除 pointSize)

分析發現:字元符號、寬高等為內部狀態 (可共享);字體大小 pointSize 為外部狀態 (不可共享)。移除 pointSize 使物件可共享:

共享字元物件

階段 3:引入享元工廠控制建立

單一職責:物件不應自管建立邏輯。CharactorFactory 管理共用池:


public class CharactorFactory {
    private Hashtable _pool = new Hashtable();
    public CharactorFactory() {
        _pool["A"] = new CharactorA(); _pool["B"] = new CharactorB(); _pool["C"] = new CharactorC();
    }
    public Charactor GetCharactor(string key) {
        if (!_pool.ContainsKey(key)) {
            switch(key) { case "A": _pool[key]=new CharactorA(); break; ... }
        }
        return (Charactor)_pool[key];
    }
}

階段 4:外部狀態參數化傳入 (破壞封裝問題)

嘗試 1:客戶端自行設定外部狀態 → 重複邏輯、破壞封裝。
嘗試 2 (正解):外部狀態透過方法參數傳入,保持封裝:


public abstract class Charactor {
    public abstract void SetPointSize(int size);
    public abstract void Display();
}
public class CharactorA : Charactor {
    private int _pointSize;
    public override void SetPointSize(int size) => _pointSize = size;
    public override void Display() => Console.WriteLine($"{_symbol} pointsize:{_pointSize}");
}

用戶端調用:


var factory = new CharactorFactory();
var ca = (CharactorA)factory.GetCharactor("A");
ca.SetPointSize(12); ca.Display();

階段 5:享元工廠單例化 (關鍵)

若建立多個工廠實例,共享池失效。工廠必須為單例:


public class CharactorFactory {
    private static CharactorFactory _instance;
    private Hashtable _pool = new Hashtable();
    private CharactorFactory() { /* 初始化池 */ }
    public static CharactorFactory Instance => _instance ?? (_instance = new CharactorFactory());
    public Charactor GetCharactor(string key) { ... }
}

單例享元工廠

四、.NET 框架內建享元:String 駐留

.NET string 內建享元機制:相同字面值共享同一物件實例。


string s1 = "abcd"; string s2 = "abcd";
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // True
string s3 = "ab"; s3 += "cd";
Console.WriteLine(Object.ReferenceEquals(s1, s3)); // False (運算時產生新物件)

五、效果、實現要點與適用性

  • 優點:大幅降低記憶體物件數量。
  • 代價:系統複雜度上升 (狀態外部化)、運行期稍增 (讀取外部狀態)。
  • 關鍵實現:嚴格區分內部/外部狀態;享元工廠單例管理共享池;外部狀態參數化傳入保持封裝。
  • 適用條件 (五者皆滿足):大量物件、記憶體壓力大、大部分狀態可外部化、可按內部狀態分組共享、系統不依賴物件身份 (不可分辨)。

六、總結

享元模式解決細粒度物件記憶體壓力,屬底層效能優化手段,實務較少見但關鍵時極具威力。掌握內外部狀態分離與工廠單例管理,即掌握享元精髓。

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