使用共享以高效地支持大量的细粒度对象。
概要
优点
- 节省内存:享元模式通过共享对象,避免了大量相似对象的创建,显著减少了内存的使用。
- 提高性能:减少对象创建和销毁的开销,提高了系统的响应速度和性能。
缺点
- 增加系统复杂度:需要将对象的状态分为内部状态和外部状态,增加了代码的复杂性和维护难度。
- 运行时性能损耗:享元工厂在管理共享对象时,如查找和创建对象,会带来一定的运行时开销,尤其是在享元对象数量众多时。
适用场景
- 对象数量巨大且大部分对象的状态可以被外部化时:例如游戏开发中大量的树木、草地、怪物、粒子等元素,它们的基本属性(如模型、纹理)可以共享,而位置、朝向等属性作为外部状态。
- 当应用程序需要创建大量细粒度对象时:如文本处理软件中的字符对象,每个字符都具有相似的属性(字体、字号等),可以通过享元模式共享这些属性
3.1 森林之树
为了在屏幕上渲染出森林,所有的(树)数据必须按照一定的方式进行组织并沿着总线从 CPU 传送到 GPU 里去
- 一个多边形
网格:定义了树干、树枝和树叶的几何描述 - 树皮和树叶的
纹理 - 树在森林中的
位置以及朝向 调节参数:如大小、颜色等,以使每棵树看起来都不一样
虽然森林中有成千上万的树木,但它们大部分看起来是相似的。它们可能会全部使用相同的网格和纹理数据

我们将对象侵害成两个独立的类——通用数据和特殊数据
通用数据:
class TreeModel
{
private Mesh mesh;
private Texture bark;
private Texture leaves;
}
整个游戏只需要一份这样的数据,然后游戏世界中每棵树的实例都有一个指向共享的 TreeModel 的引用
class Tree
{
private TreeModel model;
private Vector3 position;
private float height;
//...
}

将数据存储在内在中是个好办法,但对渲染毫无助益。在显示之前,森林数据还是必须按照一定的格式上传到 GPU 中,我们需要用显卡能够识别的方式来表达这种资源共享
3.2 一千个实例
为了最大程度地减少发送到 GPU 上的数据量,我们希望
- 只发送一次共享数据——TreeModel
- 再单独将每棵树的特殊数据上传
- 告诉 GPU:使用那个共享模型来渲染每个实例
3.3 享元模式
有太多对象并考虑对其进行轻量化时它便能派上用场
3.4 扎根之地
地面也要在我们的游戏中被表示出来。如草地、泥土、丘陵、湖泊==,我们将使用基于瓦片(Tile-based)的技术来构建地面,地面是一个由许多细小瓦片组成的巨大网格,每块瓦片都由某种地形所覆盖
class Terrain
{
private int moveCost;
private bool isWater;
private Texture texture;
Terrain(int moveCost, bool isWater, Texture texture) { ... }
//...
}
但我们不希望为每块瓦片构建地形实例付出成本,因为同类的地形实例是相同的
class World
{
private Terrain[,] tiles = new Terrain[width, height];
//...
}
每个相同地形的瓦片都会引用相同的地形实例
