【设计模式】享元模式

享元模式(Flyweight Pattern)

概念:

· 属于结构型设计模式;

· 核心思想是通过共享相同的对象减少内存消耗;

UML结构:

复制代码
+------------------+
|   Flyweight      |  <<interface/abstract>>
+------------------+
| +Operation(ext)  |
+------------------+
            ▲
            |
+-----------------------+
| ConcreteFlyweight      |
+-----------------------+
| -intrinsicState       |
+-----------------------+
| +Operation(ext)       |
+-----------------------+

+-----------------------+
| FlyweightFactory       |
+-----------------------+
| -flyweights: dict      |
+-----------------------+
| +GetFlyweight(key)    |
+-----------------------+

代码示例:

cs 复制代码
/// 享元抽象类
/// </summary>
public abstract class Flyweight
{
    public abstract void Operation(string extrinsicState);
}

/// <summary>
/// 具体享元类
/// </summary>
public class ConcreteFlyweight : Flyweight
{
    private string _intrinsicState; // 内部状态

    public ConcreteFlyweight(string intrinsicState)
    {
        this._intrinsicState = intrinsicState;
    }

    /// <summary>
    /// 操作享元类
    /// </summary>
    /// <param name="extrinsicState">外部状态</param>
    public override void Operation(string extrinsicState)
    {
        Console.WriteLine($"内部状态:{_intrinsicState}, 外部状态:{extrinsicState}");
    }
}

/// <summary>
/// 不共享的具体享元类
/// </summary>
public class UnSharedConcreteFlyweight : Flyweight
{
    private string _intrinsicState; // 内部状态

    public UnSharedConcreteFlyweight(string intrinsicState)
    {
        this._intrinsicState = intrinsicState;
    }

    /// <summary>
    /// 操作
    /// </summary>
    /// <param name="extrinsicState">外部状态</param>
    public override void Operation(string extrinsicState)
    {
        Console.WriteLine($"不共享状态:{_intrinsicState}, 外部状态:{extrinsicState}");
    }
}

/// <summary>
/// 享元工厂
/// </summary>
public class FlyweightFactory
{
    private Dictionary<string, Flyweight> _flyweightDic = new(); // 映射表

    private static readonly object factorylock = new(); // 访问映射表的锁对象

    /// <summary>
    /// 获取享元对象
    /// </summary>
    /// <param name="key">内部状态</param>
    /// <returns>享元对象</returns>
    public Flyweight GetFlyweight(string key)
    {
        Flyweight flyweight = null;
        // 若存在则直接获取
        if (_flyweightDic.TryGetValue(key, out flyweight)
            && flyweight != null)
        {
            return flyweight;
        }
        // 如果不存在或为null,则创建并添加
        lock (factorylock)
        {
            if (!_flyweightDic.TryGetValue(key, out flyweight)
            || flyweight == null)
            {
                flyweight = new ConcreteFlyweight(key);
                _flyweightDic[key] = flyweight;
            }
        }
        return flyweight;
    }

}

/// <summary>
/// 客户端
/// </summary>
public class Client
{
    public static void Main()
    {
        FlyweightFactory flyweightFactory = new FlyweightFactory();
        Flyweight concreteFlyweightA = flyweightFactory.GetFlyweight("A");
        Flyweight concreteFlyweightB = flyweightFactory.GetFlyweight("B");

        concreteFlyweightA.Operation("A的外部状态");
        concreteFlyweightB.Operation("B的外部状态");

        UnSharedConcreteFlyweight unSharedConcreteFlyweight = new("不共享的对象的内部状态");
        unSharedConcreteFlyweight.Operation("外部状态");
    }
}

特点:
优点:

· 减少内存的消耗 :对象共享后,系统中不会存在大量重复对象;

· 提高性能

· 便于系统扩展
缺点:

· 增加系统复杂性 :需要区分内部状态和外部状态,并在客户端正确管理正确的内部状态,享元工厂需要维护共享池,本身也是额外开销;

· 外部状态管理复杂 :外部状态无法被管理在享元对象中,若传入错误,很可能出现逻辑错误;

· 可能增加运行时间开销 :对于享元类的创建、查找等额外操作,带来了其他的性能开销;

· 不适合可变状态多的对象 :若对象中状态很多都不共享,使用享元模式反而本末倒置;

适用场景:

· 需要创建大量重复对象的场景

· 对象可分为内部状态和外部状态的

· 系统对性能和内存要求高的

· 对象需要共享但也希望对客户端保持透明访问的

举例

· 文本编辑器中的字符对象

问题 :文本中大量相同字符,如果每个字符都创建一个对象,内存开销巨大;

共享对象(内部状态) :字符的字体、字号、颜色、样式等;

外部状态:字符在文档中的位置(行号、列号);

收益 :相同字符只创建一次对象,减少内存占用;

· 游戏场景中的树木、石头、建筑等静态对象

问题:游戏地图中有成百上千棵树、石头或建筑,如果每个都独立占用内存,会很大;

共享对象(内部状态):模型、纹理、颜色;

外部状态 :位置、旋转、缩放;

收益:通过享元对象池共享模型和纹理,节省大量显存;

· 棋类游戏中的棋子对象

问题 :棋盘上大量棋子对象需要频繁绘制和移动,如果每个棋子都新建对象,开销大;

共享对象(内部状态) :棋子的类型(黑/白棋)、形状;

外部状态 :棋子在棋盘上的坐标;

收益:相同类型棋子只创建一次对象,移动时只更新外部状态;

相关推荐
茉莉玫瑰花茶2 小时前
C++扩展 --- 并发支持库(补充3)
开发语言·c++
一只乔哇噻3 小时前
java后端工程师进修ing(研一版‖day49)
java·开发语言
枫叶丹43 小时前
【Qt开发】输入类控件(二)-> QTextEdit
开发语言·qt
MintYouth3 小时前
【精】C# 精确判断XML是否存在子节点
xml·c#
JAVA学习通3 小时前
微服务项目->在线oj系统(Java-Spring)----[前端]
java·开发语言·前端
hrrrrb4 小时前
【Python】文件处理(二)
开发语言·python
软件黑马王子4 小时前
2025Unity超详细《坦克大战3D》项目实战案例(上篇)——UI搭建并使用和数据持久化(附资源和源代码)
游戏·ui·unity·c#
先知后行。5 小时前
QT实现计算器
开发语言·qt
掘根5 小时前
【Qt】常用控件3——显示类控件
开发语言·数据库·qt