【设计模式】享元模式

享元模式(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("外部状态");
    }
}

特点:
优点:

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

· 提高性能

· 便于系统扩展
缺点:

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

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

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

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

适用场景:

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

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

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

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

举例

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

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

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

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

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

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

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

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

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

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

· 棋类游戏中的棋子对象

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

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

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

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

相关推荐
码不停蹄Zzz10 小时前
C语言第1章
c语言·开发语言
行者9611 小时前
Flutter跨平台开发在OpenHarmony上的评分组件实现与优化
开发语言·flutter·harmonyos·鸿蒙
阿蒙Amon11 小时前
C#每日面试题-Array和ArrayList的区别
java·开发语言·c#
SmartRadio12 小时前
ESP32添加修改蓝牙名称和获取蓝牙连接状态的AT命令-完整UART BLE服务功能后的完整`main.c`代码
c语言·开发语言·c++·esp32·ble
且去填词12 小时前
Go 语言的“反叛”——为什么少即是多?
开发语言·后端·面试·go
知乎的哥廷根数学学派12 小时前
基于生成对抗U-Net混合架构的隧道衬砌缺陷地质雷达数据智能反演与成像方法(以模拟信号为例,Pytorch)
开发语言·人工智能·pytorch·python·深度学习·机器学习
yeziyfx13 小时前
kotlin中 ?:的用法
android·开发语言·kotlin
charlie11451419113 小时前
嵌入式的现代C++教程——constexpr与设计技巧
开发语言·c++·笔记·单片机·学习·算法·嵌入式
刀法如飞13 小时前
开箱即用的 DDD(领域驱动设计)工程脚手架,基于 Spring Boot 4.0.1 和 Java 21
java·spring boot·mysql·spring·设计模式·intellij-idea
古城小栈13 小时前
Rust 网络请求库:reqwest
开发语言·网络·rust