Skip to content

享元模式

通过共享大量细粒度对象,来减少内存使用,提升性能。

适合的场景

  • 系统中创建了大量类似对象,内存飙升!

  • 希望复用相同状态(共享部分),只保留个别差异作为外部状态

  • 节省对象创建成本、优化性能,尤其适合精灵图、粒子、字符串、地图块等等

基本结构

  1. Flyweight(享元):共享对象的接口,通常是不可变的

  2. ConcreteFlyweight(具体享元):实现共享逻辑

  3. FlyweightFactory(享元工厂):管理共享对象的创建和复用

  4. 外部状态:变化的部分,由调用方传入

示例

csharp
// 享元类:单位图标(共享部分)
class UnitIcon
{
    public string Texture { get; }
    public UnitIcon(string texture) => Texture = texture;

    public void Draw(int x, int y)
    {
        Console.WriteLine($"绘制单位图标 {Texture} 于位置({x}, {y})");
    }
}

// 工厂:用于管理共享的图标对象
class IconFactory
{
    private readonly Dictionary<string, UnitIcon> _icons = new();

    public UnitIcon GetIcon(string type)
    {
        if (!_icons.ContainsKey(type))
            _icons[type] = new UnitIcon($"assets/{type}.png");

        return _icons[type];
    }
}

// 使用:只存储坐标,图标统一复用
class Unit
{
    public int X { get; }
    public int Y { get; }
    public UnitIcon Icon { get; }

    public Unit(int x, int y, UnitIcon icon)
    {
        X = x;
        Y = y;
        Icon = icon;
    }

    public void Draw() => Icon.Draw(X, Y);
}

var factory = new IconFactory();
var icon = factory.GetIcon("knight"); // 所有骑士都用同一图标

var units = new List<Unit>
{
    new(0, 0, icon),
    new(1, 2, icon),
    new(3, 4, icon),
};

foreach (var unit in units)
    unit.Draw();

GDScript示例

gdscript
# 享元类:弹幕样式
class BulletType extends Resource:

    var texture_path: String

    func draw(x: int, y: int) -> void:
        print("在 (%d, %d) 绘制子弹:%s" % [x, y, texture_path])

# 工厂类
class BulletFactory:

    var _cache := {}

    func get_bullet(type_name: String) -> BulletType:
        if not _cache.has(type_name):
            var b = BulletType.new()
            b.texture_path = "res://bullet/" + type_name + ".png"
            _cache[type_name] = b
        return _cache[type_name]
# 使用
func _ready():
    var factory := BulletFactory.new()
    var bullet_type := factory.get_bullet("fireball")

    for i in 10:
        bullet_type.draw(i * 10, i * 5)