Flyweight Pattern(享元模式)

模式简介

       享元模式是一种结构型设计模式,用于通过共享对象来减少内存开销,适用于需要使用大量相似对象的情况。

       享元模式有两个状态,分别是内部状态和外部状态,内部状态由获取指定享元的调用者共享,外部状态由调用者进行传递。例如对于字符"A"而言,无论外部有多少个调用者使用字符"A",字符"A"的Unicode对于所有调用者而言都是固定一致的,而字符"A"的样式则由调用者来决定,那么字符"A"的Unicode就相当于其内部状态,字符"A"的样式则是外部状态,调用者通过字符"A"的Unicode获取字符"A"的对象,字符"A"提供一个可指定样式的绘制方法,在使用字符"A"时则可以传递不同的样式,通过享元模式就可以避免为每个调用者都创建一个字符"A"对象,从而减少内存开销。

       常见的应用场景:文本编辑器的字符存储、图形系统中的图元对象、数据库连接池、线程池、Web应用中的缓存管理等。

享元模式与对象池的对比
特征享元模式对象池
主要目标最小化相似对象的内存开销,通过共享内部状态。减少创建和销毁对象的开销,通过对象的重复利用。
对象状态分为内部状态和外部状态。内部状态可被共享,外部状态需要传递。关注对象的整体状态,重点是对象的创建和销毁开销。
内存占用减小对象实例数量,通过共享内部状态减小内存占用。通过重复使用对象减小内存占用,不一定共享内部状态。
创建和销毁开销重点不在创建和销毁开销,而在共享内部状态。重点在于减少创建和销毁对象的开销。
使用场景适用于大量相似对象,且内部状态占用大量内存的情况。适用于对象创建和销毁开销较大的情况,例如数据库连接、线程等。
实例管理通常通过工厂模式创建和管理享元对象。通常通过池管理已创建的对象实例。
操作和状态传递操作需要传递外部状态,确保每个对象的特定状态都能正确反映在操作中。操作和状态都在对象池中管理,对象之间通常不需要传递外部状态。

模式结构

  1. Flyweight(享元接口): 定义共享对象的接口,包含操作方法。

  2. ConcreteFlyweight(具体享元类): 实现享元接口,并包含内部状态。具体享元类的实例可以被共享。

  3. UnsharedConcreteFlyweight(非共享具体享元类): 与共享对象协作,但不能被共享的具体享元类,通常是因为它包含了不可共享的外部状态。

  4. FlyweightFactory(享元工厂): 负责创建和管理享元对象,确保共享对象的正确使用。在请求享元对象时,工厂检查是否已经存在该对象,如果存在则返回已有的对象,否则创建一个新对象。

  5. Client(客户端): 使用享元模式的客户端,维护或传递外部状态给享元对象。

工作原理

  1. 内部状态和外部状态: 将对象的状态分为内部状态和外部状态。内部状态是可以被共享的,而外部状态是对象在使用过程中会改变的状态,需要由客户端来维护和传递。

  2. 享元工厂: 工厂负责管理享元对象,维护一个享元池(集合)。当客户端请求一个享元对象时,工厂检查池中是否已存在该对象的实例,如果存在则返回已有的对象,否则创建一个新的对象并加入池中。

  3. 享元接口和具体享元类: 定义享元接口,包含操作方法。具体享元类实现了享元接口,包含内部状态。具体享元类的实例是可以被共享的对象。

  4. 客户端使用享元对象: 客户端在使用享元模式时,通过工厂获取享元对象,并传递外部状态。客户端负责维护外部状态,而内部状态由享元对象共享。

代码示例(C#)

提示:可在本栏目的资源篇“设计模式代码示例合集”下载所有完整代码资源。

图形:Shape.cs


namespace FlyweightPattern;

// 图形类型
enum ShapeType
{
    Circle, Rectangle
}

// 图形
abstract class Shape
{
    protected ShapeType shapeType; // 图形的类型

    public Shape(ShapeType shapeType)
    {
        this.shapeType = shapeType;
    }

    public abstract void Draw(string color); // 根据指定颜色进行绘制
}

// 圆
class Circle : Shape
{
    public Circle(ShapeType shapeType) : base(shapeType) { }

    public override void Draw(string color)
    {
        if (string.IsNullOrEmpty(color)) Console.WriteLine($"Circle:Drawing a circle.(HashCode:{GetHashCode()})");
        else Console.WriteLine($"Circle:Drawing a circle with {color} color.(HashCode:{GetHashCode()})");
    }
}

// 矩形
class Rectangle : Shape
{
    public Rectangle(ShapeType shapeType) : base(shapeType) { }

    public override void Draw(string color)
    {
        if (string.IsNullOrEmpty(color)) Console.WriteLine($"Rectangle:Drawing a rectangle.(HashCode:{GetHashCode()})");
        else Console.WriteLine($"Rectangle:Drawing a rectangle with {color} color.(HashCode:{GetHashCode()})");
    }
}

图形管理器:ShapeManager.cs


namespace FlyweightPattern;

// 图形管理器
class ShapeManager
{
    private Dictionary<ShapeType, Shape> shapes; // 图形集合

    public ShapeManager()
    {
        shapes = new Dictionary<ShapeType, Shape>();
    }

    // 获取指定类型的图形
    public Shape GetShape(ShapeType shapeType)
    {
        if (!shapes.ContainsKey(shapeType))
        {
            switch (shapeType)
            {
                case ShapeType.Circle:
                    shapes[shapeType] = new Circle(shapeType);
                    break;
                case ShapeType.Rectangle:
                    shapes[shapeType] = new Rectangle(shapeType);
                    break;
                default:
                    Console.WriteLine("Warning:The type of shape doesn't exist.");
                    break;
            }
        }
        return shapes[shapeType];
    }
}

测试代码:Program.cs

// ************* 12.享元模式测试 **************
using FlyweightPattern;
#pragma warning disable

// 创建图形管理器实例并获取圆形和矩形
ShapeManager shapeManager = new ShapeManager();
Circle circle = shapeManager.GetShape(ShapeType.Circle) as Circle;
Rectangle rectangle = shapeManager.GetShape(ShapeType.Rectangle) as Rectangle;

// 绘制红色的圆形和蓝色的矩形
circle.Draw("red");
rectangle.Draw("blue");

// 再次获取一个圆形并绘制成蓝色的圆形
Circle otherCircle = shapeManager.GetShape(ShapeType.Circle) as Circle;
otherCircle.Draw("blue");

代码解说

       上述代码模拟了对不同颜色的圆形和矩形的绘制。圆形和矩形都继承自图形类,图形类中的图形类型为享元模式中的内部状态,图形类中Draw方法的颜色参数则为外部状态。调用者通过图形管理器的GetShape方法和图形类型参数来获取图形,然后通过调用图形的Draw方法和传递颜色参数来决定绘制什么颜色的图形。测试代码中的circleotherCircle实际上为同一个实例,通过它们输出的HashCode即可知道。

如果这篇文章对你有帮助,请给作者点个赞吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值