转载

享元模式(Flyweight Pattern)

温馨提示:
本文最后更新于 2023年02月13日,已超过 604 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

flyweight-title

享元模式

运用共享技术来有效地支持大量细粒度对象的复用。

这个设计模式在 GOF 的书中是用 flyweight 这个词来定义这种模式的,然后翻译成中文就叫 享元 了,讲真挺不自在,首先这个词是一个自造词(享元)

flyweight-search.png

再者就是这个词语我认为用轻量化的解释更合适,不过现在被翻译成享元肯定是有他的原因的,至于为什么翻译享元已经不重要了,这都不会影响我们学习的对不对!

如何理解

我们先一起来理解一下这个词的意思,然后再说这个模式解决的问题,希望我的解释能帮你快速的了解这个设计模式的意图。

我是这样理解的。享元,共享单元。什么意思呢,将一些资源共享,以减少一些不必要的资源消耗。我接着举几个例子说明一下;为了代入感更强,我就拿游戏举例了。

声明:以下内容只为学习类比使用,并不代表游戏设计方案,游戏如何设计实现,我未参与,也未研究,感兴趣的可自行了解。

1. 我的世界

mc

游戏地图

我们都知道我的世界是一个自由度超高的沙盒游戏。进到游戏之后我们应该会看到一个画面,就是地图在不断的渲染。这里可能以前玩的时候大家都没有注意过,只是觉得游戏好大,但是不怎么卡。不卡的原因有很多。我们今天要说的就是如何通过享元模式来减少资源负担。

假如我的世界地图中每个单位格子的内容大小为1kb,粗略估计一个画面内格子的数量为1,000,000,此时加载地图需要 1GB 的内存,如果每个格子2kb则 2GB。如果一个单元格内容所用的贴画是 10kb 呢。目前来看 10G 内存也都能接受,可这款游戏放在当年的话,估计不会有人玩了。

如何解决

其实这个方案非常的正常,也非常的简单。首先我们可以这样做,事先将需要用到的格子贴画统计好,然后一次加载到内存中,记录一下内存的地址,需要用的时候,直接取出来渲染就好了。他们的样子都差不多,只是摆放的位置不同。还有一种方式呢,就是我用一个先去我的 资源库 找,找不到就创建一个放到资源库中,如果能够找到,就直接返回。这两种方式都可以。第一种方式将压力放在了启动过程,第二种的方式将压力放在第一次渲染的过程。而一般情况下,游戏的开发都是用第一种方式,也就是我们所说的“过图”,”地图加载“。这个时候去做的,因为一次卡顿加载完和你走着走着卡一下当然第一种更容易接受。

2.英雄联盟

lol

英雄联盟这款游戏大家应该并不陌生,S10 刚刚结束(10月31日全球总决赛),SN来年再战,加油。

“兵线”

游戏中一共有 3 路兵线,每次出现几只我不清楚,8只好了。 3路乘以2(双方)然后在乘以8,这应该是48个对象。而且他们还包含各自的动作,比如魔法兵吐得“口水“,炮车喝奶茶吐的“珍珠”等等,如果是你在开发兵线系统的时候,内存爆炸了,比如有的玩家搞怪,不杀小兵,积攒了很多小兵,然后他卡了,说你游戏垃圾。你该如何去做呢。

其实我们分析下来的话,这里只会出现三种不同的兵种,步兵、魔法兵、炮车。然后再分为红蓝两方。在加上两个子弹。是不是就只有这8个对象呢,至于他们的轨迹,那些是每个对象的“外部状态”

如何构成

知道了这种设计模式思路,就要继续了解一下享元模式具体的构成角色都有哪些了。比如以英雄联盟的兵线为例吧。

客户端

首先有一个客户端,负责获取对象,然后渲染,这里我们通过#get、#draw(x,y)来表示获取和画来代替这步动作,(x,y)表示渲染出来的对象坐标。

享元工厂

然后还有一个为我们提供小兵的统一接口,这里使用的就是我们前面学习的工厂方法,小兵工厂。这里顺便复习一下之前的工厂和抽象工厂两个设计模式。如果我通过一个工厂来实现小兵对象的创建,那么就是一个工厂模式,但是我现在想在应用的时候,在灵活一些,我们可以从小兵身上抽取特征,比如步兵、魔法兵、炮车、这是小兵类别,但我们有两个不同的作战方,红方和蓝方,所以此时可以使用抽象工厂模式来生产小兵,红方小兵工厂生产出来的都是红方的步兵、魔法兵、炮车。蓝方生产出来的就是蓝方的步兵、魔法兵、炮车。

享元对象

有了工厂之后,我们就要有具体的共享对象了,共享对象就是我们上面所说的那 8 个。

下面这个类图顺便复习了一下 抽象工厂模式

flyweight-bingxian

享元模式类图 📌

flyweight.png

代码 📃

下面就使用 享元模式 来模拟一下英雄联盟的兵线的开发。

flyweight-coder.png

flyweight-test.png

关注回复 “源码” 获取享元模式创建LOL兵线代码。

总结 📚

  1. 通过享元模式可以让我们用更小的空间来构造一个更大对象。这也是利用了池技术来实现的。
  2. 使用享元模式可以有效的缓解内存使用的问题。
  3. 你会发现,当你有外部状态的时候(具体体现在红蓝两方在创建小兵对象的时候,需要指定颜色),享元模式会变得稍显复杂。

其他设计模式:点击查看

正文到此结束
本文目录