首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >漫谈模式之享元模式

漫谈模式之享元模式

作者头像
孟君
发布2023-03-16 06:35:24
4470
发布2023-03-16 06:35:24
举报

今天,我们来分享结构型模式的另外一个成员:享元模式

享元模式是一种结构型模式,它通过共享尽可能多的对象来减少内存使用和对象创建的数量,从而提高系统性能和效率。

在开始本文介绍之前,我们先来看一个题目:

图片
图片

问题来了?

  • v1 ==  v2会输出啥true or false? 
  • v3 == v4 会输出啥true or false ? 

答案是输出

true
false

为什么呢?

第一个输出true,是因为-128到127的数直接取自Cache,所以是同一个对象。

图片
图片

采用共享技术是享元模式的特点。接下来,我们一起来看下享元模式的介绍和示例。

享元模式介绍

意图

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

结构

享元模式的基本结构如下:

这里涉及到的参与者有如下几种:

抽象享元角色(Flyweight)

  • 此角色是所有的具体享元的超类,为这些类规定出需要实现的公共接口。那些需要外部状态的操作可以通过方法的参数传入

具体享元角色(ConcreteFlyweight)

  • 实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内部共享。

非共享享元角色(UnsharedConcreteFlyweight)

  • 并非所有Flyweight的子类都需要被共享。Flyweight接口使共享成为可能,但它并不是强制共享。在Flyweight对象结构的某些层次,UnsharedFlyweight对象通常将ConcreteFlyweight对象作为子节点。

享元工厂(FlyweightFactory)

  • 创建并管理享元对象。
  • 确保合理地共享Flyweight。当用户请求一个Flyweight的时候,享元工厂对象提供一个已创建的实例或者创建一个(如果不存在的话)

客户端(Client)

  • 维持一个对Flyweight对象的引用。
  • 计算或者存储一个(多个)Flyweight的外部状态。

享元模式示例

示例一、茶馆小憩喝茶

春暖花开,我们以一群好朋友周末去西湖游玩,然后在西湖边茶馆喝茶闲聊为场景,给出一个简单的享元模式示例。

每个口味的茶对象是可以共享的,同口味的茶可以服务与不同桌号的顾客。

抽象享元角色

图片
图片

具体享元角色

图片
图片

非共享享元角色

此Context非常简单,本来还想加一个楼层信息的属性,简单化了,就只保留了一个桌号属性。TeaOrder参数也可以简化成一个int类型的tableNumber参数。

享元工厂

根据口味获取茶对象。如cache存在则直接从cache返回;如没有,则新建并放入map缓存起来,下次直接从缓存中获取,达到共享的效果。

图片
图片

Client.java(客户端)

Client端使用Factory来获取茶对象。

图片
图片

某次输出结果:

图片
图片

至此一个简单的享元模式例子就完成了。

从上面输出的结果来看,10个人点了十杯茶,包括3种口味,我们只创建了3个茶口味的对象,有7杯茶用到的Tea对象来自于共享

示例二、坦克大战

小时候玩过的2D的坦克大战小游戏,它可以使用享元模式来提高性能和减少内存占用。比如地图中有很多的障碍物,可以是砖墙、草地、河流等,这些障碍物的特点是完全共享的,只是在不同的坐标位置显示出来即可。

图片
图片

我们以这些障碍物作为示例,再写一个简单的享元模式实现。

障碍物享元接口

图片
图片

具体障碍物享元

图片
图片

享元工厂

图片
图片

障碍物享元工厂,它负责创建和管理障碍物对象。

游戏客户端

Client端使用Factory来获取障碍物对象,然后使用障碍物对象的方法来绘制游戏场景。由于相同类型的障碍物共享相同的对象,因此可以有效地减少内存占用和对象创建。

图片
图片

某次运行结果:

图片
图片

由结果可见,我们渲染了很多障碍物,但是,其实也只有创建了3个对象,3个对象都会共享使用。同一类型的障碍物唯一的区别是显示的坐标一致。如果同类的障碍物显示的量很大,内存中其实只要维护少量的对象数量即可。

小结

优点:

1、大幅度降低内存中对象的数量。

缺点:

1、享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。

2、享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。

线程池是享元模式的一种应用

在 Java 中,线程池是一种可以重用多个线程的机制,它能够提高程序的性能和效率。在使用线程池时,可以使用享元模式来管理线程的创建和共享,从而更加有效地利用系统资源。

具体来说,线程池中的线程对象可以看作是享元模式中的共享对象,而线程池本身则是享元工厂。当需要使用线程时,线程池从共享池中获取一个空闲的线程对象,而不是每次都创建新的线程对象,这样可以避免频繁地创建和销毁线程对象,从而提高系统性能和效率。

最后,再来看点原型模式和享元模式的一些区别。

原型模式和享元模式的区别

原型模式和享元模式都是为了提高系统性能和效率而设计的模式,但它们的应用场景和实现方式不同,需要根据具体情况进行选择。

应用场景

原型模式适用于创建复杂对象的情况,即需要耗费大量时间和资源的对象。通过克隆已有的对象,可以避免重新创建对象的过程,提高系统的性能和效率。

享元模式则适用于需要创建大量相似对象的情况,通过共享对象的内部状态,可以减少系统中对象的数量,从而节省内存空间和运行时的开销。

实现方式 原型模式的实现方式是通过克隆已有的对象来创建新的对象。实现 Cloneable 接口,并重写 clone() 方法,然后在需要的地方调用 clone() 方法创建新对象。

享元模式则需要将对象的内部状态和外部状态分离开来。内部状态是对象共享的部分,外部状态是对象不同的部分。通过将内部状态保存在共享池中,多个对象可以共享同一个内部状态,而将外部状态保存在对象的属性中,每个对象可以拥有自己的不同属性。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 享元模式介绍
  • 享元模式示例
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档