享元模式
享元模式是对象结构型模式,此模式为了处理大量的细粒度对象所导致的内存不足的解决方案。它通过运用共享技术来支持大量细粒度对象的复用,它通过共享已经存在的对象来大幅减少需要创建的对象数量(主要是内部状态的数量),从而避免大量的相似类的开销。
其中线程池,字符串池等,是最为常见的运用。在游戏开发中,我们需要对游戏中大量的对象的创建进行复用,其中包括粒子,模型等,大多数都是通过享元模式。这个模式对于游戏开发十分重要。
结构
说明
- 抽象享元(Flyweight)- 定义子享元类的公有方法,且方法本身依赖于外部状态,通过参数进行数据交互。
- 具体享元(Concrete Flyweight)- 实现具体方法,并将内部状态初始化。
- 享元工厂(Flyweight Factory)- 负责创建和管理享元对象,本身可作为单例。
- 外部状态(External State)- 享元对象需要的数据,但其本身会变化,将它以外部参数的形式传入,从而完成方法。
享元模式中,为了追求对象共享,我们将对象的信息分为两个部分
- 内部状态 :指对象共享出来的部分,本身不会改变。
- 外部状态:指对象中会随环境改变的变量,他们不存在享元对象中,而是通过参数传入。
实现
这里我们举一个游戏粒子的例子。
抽象粒子 - 抽象享元(定义内部状态)
public abstract class ParticleFlyweight
{
protected readonly string _name;
protected readonly float _size;
protected readonly float _lifeTime;
protected ParticleFlyweight(string name, float size, float lifeTime)
{
_name = name;
_size = size;
_lifeTime = lifeTime;
}
public abstract void Release(ExternalStateParticle externalState);
}
粒子1,2 - 具体享元
public class Particle1 : ParticleFlyweight
{
public Particle1() : base(
"Particle1",
10f,
3f)
{
}
public override void Release(ExternalStateParticle externalState)
{
Debug.Log(_name + "粒子,大小为" + _size + ",在" + externalState.Position +
"位置,旋转为" + externalState.Quaternion + ",速度为" +externalState.Speed +
",生命周期为" + _lifeTime + ",释放");
}
}
public class Particle2 : ParticleFlyweight
{
public Particle2() : base(
"Particle2",
5f,
5f)
{
}
public override void Release(ExternalStateParticle externalState)
{
Debug.Log(_name + "粒子,大小为" + _size + ",在" + externalState.Position +
"位置,旋转为" + externalState.Quaternion + ",速度为" +externalState.Speed +
",生命周期为" + _lifeTime + ",释放");
}
}
外部粒子 - 外部状态
public struct ExternalStateParticle
{
public Vector3 Position;
public Quaternion Quaternion;
public float Speed;
public ExternalStateParticle(Vector3 position, Quaternion quaternion, float speed)
{
this.Position = position;
this.Quaternion = quaternion;
this.Speed = speed;
}
}
粒子工厂 - 享元工厂
public class ParticleFactory
{
private Dictionary<string, ParticleFlyweight> _particle;
private static ParticleFactory _instance = new ParticleFactory();
public static ParticleFactory Instance => _instance;
private ParticleFactory()
{
_particle = new Dictionary<string, ParticleFlyweight>();
_particle.Add("1", new Particle1());
_particle.Add("2", new Particle2());
}
public ParticleFlyweight GetParticleFlyweight(string key) =>
key switch
{
"1" => _particle["1"],
"2" => _particle["2"],
_ => throw new ArgumentException("key 这个参数不存在"),
};
}
粒子 - (内部状态 + 外部状态 = 完整粒子)
public class Particle
{
private ParticleFlyweight _internalState;
private ExternalStateParticle _externalState;
public Particle(ParticleFlyweight internalState, ExternalStateParticle externalState)
{
_internalState = internalState;
_externalState = externalState;
}
public void Release() => _internalState.Release(_externalState);
}
调用
public class FlyweightExample : MonoBehaviour
{
private void Start()
{
ParticleFlyweight internalState1 = ParticleFactory.Instance.GetParticleFlyweight("1");
ParticleFlyweight internalState2 = ParticleFactory.Instance.GetParticleFlyweight("2");
ExternalStateParticle externalState1 = new ExternalStateParticle(
new Vector3(0f, 0f, 0f),
new Quaternion(0f, 0f, 0f, 1),
1.0f);
ExternalStateParticle externalState2 = new ExternalStateParticle(
new Vector3(0f, 0f, 0f),
new Quaternion(0f, 0f, 0f, 1),
2.0f);
Particle particle1 = new Particle(internalState1, externalState1);
Particle particle2 = new Particle(internalState2, externalState2);
particle1.Release();
particle2.Release();
}
}
应用场景
- 当程序中存在大量的相似的对象时,即大量细粒度对象,为节约内存容量,应该使用享元模式。
利与弊
优点
- 节约资源,避免创建大量的相同的对象数据。
缺点
- 由于享元模式将原本完整的对象数据分成两部分,导致执行方法过程,每次都需传入数据,增加执行时间。
- 容易使系统复杂。