8.享元模式

8.结构型模式-享元模式


8.1 基础知识

学习难度:4

使用频率:1

总分:3

定义

享元模式(Flyweight Pattern)是一种运用共享技术有效地支持大量细粒度的对象的设计模式。

说人话

通过享元工厂的享元池共享内部状态相同的对象,对对象传入外部状态进行不同的操作。

结构图

实现步骤

  • 享元接口:定义操作方法,可以包含外部状态参数。
  • 具体享元类:实现享元接口,私有内部状态并在构造函数中传入初始化,实现操作方法。
  • 享元工厂类:定义享元字典并实例化,创建和管理享元对象。定义获得享元的方法,传入内部状态判断享元字典中是否包含该内部状态的享元对象。如果包含,则直接返回;否则,实例化新的该内部状态的享元对象添加到享元字典再返回。
  • 使用时:实例化享元工厂,传入不同的内部状态以获取享元对象,对享元对象传入不同的外部状态执行操作方法。

说明

其实 ScriptableObject 就算是实现了享元模式,需要清楚地认识内部状态和外部状态。


8.2 模版代码

享元接口和具体享元类

// 享元接口
interface IFlyweight
{
    void Operation(int extrinsicState);
}

// 具体享元类
class ConcreteFlyweight : IFlyweight
{
    private string intrinsicState;

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

    public void Operation(int extrinsicState)
    {
        Console.WriteLine($"具体享元对象:内部状态 = {intrinsicState}, 外部状态 = {extrinsicState}");
    }
}

享元工厂类

// 享元工厂
class FlyweightFactory
{
    private Dictionary<string, IFlyweight> flyweights = new Dictionary<string, IFlyweight>();

    public IFlyweight GetFlyweight(string key)
    {
        if (flyweights.ContainsKey(key))
        {
            return flyweights[key];
        }
        else
        {
            IFlyweight flyweight = new ConcreteFlyweight(key);
            flyweights[key] = flyweight;
            return flyweight;
        }
    }
}

客户端

class Program
{
    static void Main()
    {
        FlyweightFactory factory = new FlyweightFactory();

        //传入内部状态值获得享元
        IFlyweight flyweight1 = factory.GetFlyweight("A");
        IFlyweight flyweight2 = factory.GetFlyweight("B");
        IFlyweight flyweight3 = factory.GetFlyweight("A");

        //传入外部状态值使用享元进行操作
        flyweight1.Operation(1);// 具体享元对象:内部状态 = A, 外部状态 = 1
        flyweight2.Operation(2);// 具体享元对象:内部状态 = B, 外部状态 = 2
        flyweight3.Operation(3);// 具体享元对象:内部状态 = A, 外部状态 = 3

        Console.WriteLine("flyweight1 和 flyweight3 是相同的对象吗? " + (flyweight1 == flyweight3));
        // flyweight1 和 flyweight3 是相同的对象吗? True
    }
}

8.3 CSharp实践

实践需求

使用享元模式,模拟用户访问网站。网站包含网站名,用户包含用户名。

用户类

// 用户类,代表外部状态
class User
{
    public string Name { get; }

    public User(string name)
    {
        Name = name;
    }
}

网站接口和具体网站类

// 网站接口
interface IWebsite
{
    void Use(User user);
}

// 具体网站类
class ConcreteWebsite : IWebsite
{
    private string name;

    public ConcreteWebsite(string name)
    {
        this.name = name;
    }

    public void Use(User user)
    {
        Console.WriteLine($"网站名称: {name}, 用户: {user.Name}");
    }
}

网站工厂

// 网站工厂
class WebsiteFactory
{
    private Dictionary<string, IWebsite> websites = new Dictionary<string, IWebsite>();

    public IWebsite GetWebsite(string name)
    {
        if (websites.ContainsKey(name))
        {
            return websites[name];
        }
        else
        {
            IWebsite website = new ConcreteWebsite(name);
            websites[name] = website;
            return website;
        }
    }
}

客户端

class Program
{
    static void Main()
    {
        WebsiteFactory factory = new WebsiteFactory();

        // 创建不同的网站
        IWebsite google = factory.GetWebsite("谷歌");
        IWebsite baidu = factory.GetWebsite("百度");

        // 不同用户使用网站
        User xiaocai = new User("小菜");
        User daniao = new User("大鸟");

        google.Use(xiaocai);// 网站名称: 谷歌, 用户: 小菜
        google.Use(daniao);// 网站名称: 谷歌, 用户: 大鸟

        baidu.Use(xiaocai);// 网站名称: 百度, 用户: 小菜
        baidu.Use(daniao);// 网站名称: 百度, 用户: 大鸟
    }
}

8.4 Unity实践

实践需求

使用划线组件生成每列每行坐标系,颜色要不同。

线内部状态

//线内部状态
public class LineInternalState
{
    private float width;
    private Material material;
    private Color color;

    public float Width => width;
    public Material Material => material;
    public Color Color => color;

    public LineInternalState(float width, Material material, Color color)
    {
        this.width = width;
        this.material = material;
        this.color = color;
    }
}

线享元接口和线具体享元类

//线享元接口
public interface ILineFlyweight
{
    //划线方法 传入外部状态起始位置和结束位置
    void DrawLine(Vector3 start, Vector3 end);
}

// 线具体享元类
public class ConcreteLineFlyweight:ILineFlyweight
{
    //内部状态
    private LineInternalState lineInternalState;
    
    //构造函数时就要赋值内部状态
    public ConcreteLineFlyweight(LineInternalState lineInternalState)
    {
        this.lineInternalState = lineInternalState;
    }
    
    //start和end是外部状态 其他参数需要的都从内部状态拿
    public void DrawLine(Vector3 start, Vector3 end)
    {
        //通用设置
        //实例化一个游戏对象
        GameObject gameObject = new GameObject($"start:{start} end:{end}");
        // 初始化LineRenderer组件
        var lineRenderer = gameObject.AddComponent<LineRenderer>();
        // 设置线的顶点数为2
        lineRenderer.positionCount = 2;
        
        //内部状态
        //设置线的颜色
        lineRenderer.startColor = lineInternalState.Color;
        lineRenderer.endColor = lineInternalState.Color;
        //设置宽度
        lineRenderer.startWidth = lineInternalState.Width;
        lineRenderer.endWidth = lineInternalState.Width;
        //设置材质
        lineRenderer.material = lineInternalState.Material;
        
        //外部状态
        //设置线的端点
        lineRenderer.SetPosition(0, start);
        lineRenderer.SetPosition(1, end);
    }
}

线享元工厂

//线享元工厂
public class LineFlyweightFactory
{
    // 用一个字典来存储线享元对象,键为内部状态,值为享元对象
    private Dictionary<LineInternalState, ILineFlyweight> lineFlyweights = new Dictionary<LineInternalState, ILineFlyweight>();
    
    // 获取或创建一个线享元对象,如果已存在则直接返回,否则创建一个新的线享元对象并加入字典中
    public ILineFlyweight GetLineFlyweight(LineInternalState lineInternalState)
    {
        if (lineFlyweights.ContainsKey(lineInternalState))
        {
            return lineFlyweights[lineInternalState];
        }
        else
        {
            ILineFlyweight lineFlyweight = new ConcreteLineFlyweight(lineInternalState);
            lineFlyweights.Add(lineInternalState, lineFlyweight);
            return lineFlyweight;
        }
    }
}

客户端

public class TestFlyweightPattern : MonoBehaviour
{
    // 定义x和z的边界值
    private int xBoundaryValue = 10;
    private int zBoundaryValue = 10;

    private void Start()
    {
        // 创建享元工厂
        LineFlyweightFactory lineFlyweightFactory = new LineFlyweightFactory();

        // 创建x和z的内部状态,然后通过工厂获取相应的享元
        
        LineInternalState xLineInternalState = new LineInternalState(0.01f, new Material(Shader.Find("Sprites/Default")), Color.white);
        ILineFlyweight xLineFlyweight = lineFlyweightFactory.GetLineFlyweight(xLineInternalState);
        
        LineInternalState zLineInternalState = new LineInternalState(0.01f, new Material(Shader.Find("Sprites/Default")), Color.red);
        ILineFlyweight zLineFlyweight = lineFlyweightFactory.GetLineFlyweight(zLineInternalState);

        // 传入参数绘制由白色和红色线条组成的网格
        DrawGrid(xBoundaryValue, zBoundaryValue, xLineFlyweight, zLineFlyweight);
    }

    //绘制网格
    private void DrawGrid(int xBoundaryValue, int zBoundaryValue, ILineFlyweight xLineFlyweight, ILineFlyweight zLineFlyweight)
    {
        //绘制x轴
        for (int x = -xBoundaryValue; x <= xBoundaryValue; x++)
        {
            Vector3 start = new Vector3(x, 0, -zBoundaryValue);
            Vector3 end = new Vector3(x, 0, zBoundaryValue);
            xLineFlyweight.DrawLine(start, end);
        }

        //绘制z轴
        for (int z = -zBoundaryValue; z <= zBoundaryValue; z++)
        {
            Vector3 start = new Vector3(-xBoundaryValue, 0, z);
            Vector3 end = new Vector3(xBoundaryValue, 0, z);
            zLineFlyweight.DrawLine(start, end);
        }
    }
}

运行结果



转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏