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