Skip to content

享元模式:如果让你开发英雄联盟的兵线,你会怎么设计?

lvgo edited this page Nov 14, 2020 · 3 revisions

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. 你会发现,当你有外部状态的时候(具体体现在红蓝两方在创建小兵对象的时候,需要指定颜色),享元模式会变得稍显复杂。

目录

首页_HOME

1. 单例模式: 资源!要合理的分配使用!

2. 原型模式:啥?盗图、盗文章的人居然用的是一种设计模式!原型模式?

3. 工厂模式:像工厂一样创建对象,让业务代码更专注!

4. 抽象工厂模式:抽象工厂模式和工厂模式有区别吗?

5. 建造者模式:学个设计模式还和人生扯上关系了?

6. 代理模式:有什么问题跟我律师说吧!

7. 装饰者模式:玩了把坦克大战居然彻底搞懂了装饰者模式!

8. 桥接模式:这个不常用的设计模式居然被我学的最透,草率了!

9. 适配器模式:今天轻松点,就说说什么是“榫”,什么是“卯”,什么是“榫卯”!

10. 外观模式:书生的家书是谁送的?书童到底是个什么角色?

11. 享元模式:如果让你开发英雄联盟的兵线,你会怎么设计?

12. 组合模式:使用组合模式做一个简单的推荐功能

13. 策略模式:学习JDK的比较器架构是如何设计的

14. 模板方法模式:你知道AQS它是干什么的吧,那这个框架是怎么设计的呢?

15. 观察者模式:原来观察者模式是JDK与生俱来的

16. 责任链模式:“张三为了纪念王二请假的悲催经历想出来的一种设计模式”

17. 备忘录模式:这款游戏你玩过吗?是不是经常”重来“?

18. 迭代器模式:你真的“会”遍历list吗?

19. 命令模式:如果把请求变成一个对象,在一些场景更好用!

20. 状态模式:从工作状态,再到订单状态一点点深入学习状态模式

21. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。

22. 访问者模式:是“凡尔赛”让我“认清”了访问者模式!

23. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。


个人博客 Star Dust

Clone this wiki locally