共计 2640 个字符,预计需要花费 7 分钟才能阅读完成。
Flyweight(享元模式)
Flyweight(享元模式)属于结构型模式,是一种共享对象的设计模式。
用意:使用共享技术无效地反对大量细粒度的对象。
举例子
如果看不懂下面的用意介绍,没有关系,设计模式须要在日常工作里用起来,联合例子能够加深你的了解,上面我筹备了三个例子,让你领会什么场景下会用到这种设计模式。
富文本编辑器的字母对象
富文本编辑器在英文环境下,其中的文本由大量字母组成,为了便于做对立的格式化、计算等解决,须要将每个字母都存储为对象,但这样存储的代价太大了。
已知英文字母一共 26 个,所以文档中存在大量重复使用的字母,而每个字母除了地位信息外,其它信息都是雷同且只读的,那么有方法升高富文本场景微小的字母对象数量吗?
网盘存储
当咱们上传一部电影时,有时候几十 GB 的内容不到一秒就上传完了,这是网盘提醒你,“已采纳极速技术秒传”,你会不会心生纳闷,这么厉害的技术为什么不能每次都失效?
另外,网盘存储时,同一部电影可能都会寄存在不同用户的不同文件夹中,而且电影文件又特地微小,和富文本相似,电影文件也只有寄存地位是不同的,而其余内容都特地微小且只读,有什么方法能优化存储呢?
大型多人游戏
玩多人游戏时,为了避免外挂,个别对象的创立与计算是在服务器实现的,那如何保障一个玩家拾取物品后,另一个玩家看到的物品会隐没?
其实情理曾经显而易见了,尽管在不同客户端之间,游戏对象是互相独立的,但在一局游戏中,所有玩家的对象在服务器是共享的。
用意解释
“共享”就是享元模式的精华,将那些大量的,具备很多外部状态而内部状态很少的对象进行共享,就是享元模式的应用形式。
用意:使用共享技术无效地反对大量细粒度的对象。
共享技术能够了解为缓存,当一个对象创立后,再次拜访雷同对象时,就不再创立新的对象了,而只有在拜访没有被缓存过的对象时,才创立新对象,并立刻缓存起来。
这样做能够无效反对大量细粒度的对象,在富文本例子中,有数的字母就是大量细粒度对象 ,在网盘存储中, 电影文件就是大量细粒度对象 ,在大型多人游戏中, 每局游戏内存在大量细粒度对象。
这些细粒度对象都领有雷同的特色:
- 量特地大,这个很容易了解。
具备大量外部状态,且不随着客户端的不同而扭转。
- 富文本的字母,不因为展现到不同语句中而发生变化,变动的只有状态;电影文件,不因为放在不同用户的文件夹中而对电影内容产生变动,变动的只有属于哪些用户,放在哪些文件夹里;多人游戏中,同一把武器对象,不因为有多集体的电脑独立运行而领有更多的弹药,变动的只有在哪些客户端被拜访。
- 具备大量内部状态,甚至没有内部状态。在下面曾经解释了,字母的地位、电影的地位、游戏对象的客户端都是内部状态,这些内部状态相比于其外部状态来说,大小微不足道,且不便拆散存储。
遇到这种状况,咱们就能够将对象外部状态共享,内部状态独立存储,从而节俭大量空间。
尤其是对于网盘的场景,承诺给用户 2 TB 的存储空间,这个用户看到其他人分享了 100 个电影,就点击“下载到我的网盘”,此时尽管占用了本人 1 TB 的网盘空间,但实际上网盘运营商并没有减少 1 TB 的存储空间,理论可能减少了 1kb 的存储空间,记录了存储地位,这就是网盘鸡贼的中央,并不占用空间的内容,却占用了用户真金白银购买的存储空间。
当然,这就是享元模式的价值,对网盘公司来说,价值微小,对用户来说,没有价值。所以享元模式的价值体现在全局,比方对整个富文本编辑器来说,缩小了巨量字母对象数量,但对于每一个字母对象而言,并没有任何优化。
结构图
对于 Client 而言,下图形容了如何共享 Flyweight:
- Flyweight: 共享接口,通过这个接口能够操作对象的内部状态。
- ConcreteFlyweight: 实现 Flyweight 接口的对象,这个对象是可被共享的。
- UnsharedConcreteFlyweight: 不被共享的对象,因为在享元模式中,实际上并不是所有对象都能够被共享。
- FlyweightFactory: 创立并治理 Flyweight 对象,通过其返回的 Flyweight 对象,如果已创立,则会返回之前创立的那个,没有的话才会创立一个新的。
- Client: 应用 Flyweight 的客户端。
通过第二个图能够显著看到,两个不同的 Client 持有了雷同 aConcreteFlyweight
援用。
代码例子
上面例子应用 typescript 编写。
`class FlyweightFactory {
public getFlyWeight(key) {
if (this.flyweight[key]) {
return this.flyweight[key]
}
const flyweight = new Flyweight()
this.flyweight[key] = flyweight
return flyweight
}
}
`
FlyweightFactory
提供的 getFlyWeight
办法,实际上是依照 key
对 flyweight
实例进行缓存,雷同 key
下只存储一个 flyweight
实例。
弊病
如果细粒度对象不多,则没必要应用享元模式。
另外,就算细粒度对象很多,如果对象外部状态并不多,次要都是内部状态,那么享元模式就起不到什么作用了,因为享元模式通过共享对象,只能节俭外部状态,而不能节俭内部状态。
另外,如果享元模式映射到的共享对象数量并没有比原始对象少出数量级关系,应用的意义也不大。比方富文本编辑器的例子,对于英文来说,一共就 26 个字母,那么 1 万字的文章优化比例是 10000:26,但对于中文文章而言,文字实例自身就很多,可能 1 万字的文章中,汉字去重后仍然有 3000 个,那么优化比例就是 10000:3000,此时享元模式的意义就没那么打了。
总结
享元模式的实质就是尽可能的共享对象,特地实用于存在大量细粒度对象,而这些对象外部状态特地多,内部状态较少的场景。
对于云存储来说,享元模式是必须应用的,因为云存储的场景决定了,存在大量细粒度文件对象,而存在大量只读的文件,就非常适合共享一个对象,每个用户存储的只是援用。
探讨地址是:精读《设计模式 – Flyweight 享元模式》· Issue #290 · dt-fe/weekly
如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 – 帮你筛选靠谱的内容。
关注 前端精读微信公众号
版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)