在享元模式里,内部状态和外部状态是两个关键概念

在享元模式里,内部状态和外部状态是两个关键概念,它们的合理划分有助于实现对象的共享,进而提升系统性能并减少内存占用。下面结合围棋游戏的例子详细解释:

内部状态

  • 定义:内部状态指的是对象固有的、不随环境改变而改变的状态,它可以被多个对象实例共享。在享元模式中,内部状态是存储在享元对象内部的,并且在对象创建之后就不会再发生变化。
  • 围棋游戏示例:在围棋游戏里,棋子的颜色(黑或白)就是内部状态。无论棋子放置在棋盘的哪个位置,其颜色始终保持不变,而且相同颜色的棋子具有相同的属性。所以,我们可以将棋子颜色作为共享的部分,只需创建少量代表不同颜色的棋子对象实例,就能够供多个棋子使用。比如,我们可以创建一个“黑色棋子”对象和一个“白色棋子”对象,棋盘上所有黑色棋子都共享“黑色棋子”对象,所有白色棋子都共享“白色棋子”对象。这样做避免了为每个棋子都创建一个独立的对象,从而减少了内存的使用。

外部状态

  • 定义:外部状态是指随环境改变而改变的状态,它不能被共享,需要在使用享元对象时动态传入。外部状态通常与具体的使用场景相关,不同的使用场景下,外部状态的值可能不同。
  • 围棋游戏示例:棋子在棋盘上的位置就是外部状态。每个棋子在棋盘上的位置是独一无二的,并且会随着游戏的进行而发生变化。由于位置信息不能被共享,所以在使用棋子对象时,需要将位置信息作为外部状态传入。例如,当我们要在棋盘上放置一个黑色棋子时,会先获取共享的“黑色棋子”对象,然后再传入该棋子要放置的具体位置(如第 3 行第 5 列),这样就能在正确的位置显示该棋子。

结合享元模式的优势

通过将棋子的颜色作为内部状态进行共享,将棋子的位置作为外部状态在使用时传入,我们可以显著减少对象的创建数量。如果不采用享元模式,每个棋子都需要一个独立的对象来存储颜色和位置信息,那么对于一个 19x19 的围棋棋盘,就需要创建 361 个棋子对象。而使用享元模式后,只需要创建 2 个代表不同颜色的棋子对象,大大降低了内存开销。同时,在处理棋子的移动和显示时,只需更新外部状态(位置信息),而不需要重新创建对象,提高了系统的性能和效率。

以下是对享元模式这些注意事项和细节的详细解释:

1)在享元模式这样理解,“享”就表示共享,“元”表示对象

享元模式的核心思想就是共享对象,以减少系统中对象的数量,从而节省内存和提高性能。“享”体现了共享的概念,即多个地方可以共同使用同一个对象实例;“元”代表对象,也就是那些被共享的实例。通过共享这些对象,避免了重复创建相同的对象,达到资源复用的目的。例如,在一个文本编辑器中,相同字符的显示样式对象可以被多个字符共享,而不是为每个字符都创建一个新的样式对象。

2)系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式

当系统中存在大量相似对象,并且这些对象占用了大量的内存资源时,使用享元模式可以有效地减少内存开销。对象的状态可以分为内部状态和外部状态,内部状态是对象固有的、不随环境变化的状态,而外部状态是随环境变化的状态。如果对象的大部分状态可以作为外部状态,那么就可以将这些对象的内部状态进行共享,只在需要时传入外部状态。例如,在一个围棋游戏中,棋子的颜色(黑或白)是内部状态,可以共享;而棋子在棋盘上的位置是外部状态,需要在使用时传入。

3)用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用 HashMap/HashTable 存储

在享元模式中,通常会使用一个享元工厂来管理和创建享元对象。为了实现对象的共享,需要为每个享元对象分配一个唯一的标识码。当客户端请求一个享元对象时,享元工厂会根据这个标识码在存储结构(如 HashMapHashTable)中查找是否已经存在该对象。如果存在,则直接返回该对象;如果不存在,则创建一个新的对象并将其存储到存储结构中,同时分配一个唯一的标识码。这样可以确保相同内部状态的对象只被创建一次,实现对象的共享。例如,在一个图形绘制系统中,每种颜色和形状的组合可以作为一个唯一的标识码,享元工厂根据这个标识码来管理和返回相应的图形对象。

4)享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率

由于享元模式通过共享对象,避免了大量重复对象的创建,因此可以显著减少系统中对象的数量,从而降低了程序的内存占用。同时,减少对象的创建和销毁操作也可以提高系统的性能,因为对象的创建和销毁是比较消耗资源的操作。例如,在一个网页浏览器中,对于相同的字体、颜色等样式对象进行共享,可以减少内存的使用,提高页面的加载速度。

5)享元模式提高了系统的复杂度,需要分离出内部状态和外部状态。而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方

使用享元模式需要对对象的状态进行仔细分析,将其划分为内部状态和外部状态。这增加了系统的设计和实现复杂度,因为需要考虑如何合理地划分状态,以及如何在使用享元对象时正确地传入外部状态。同时,外部状态应该具有固化特性,即不应该随着内部状态的改变而改变。如果外部状态依赖于内部状态,那么在共享对象时可能会导致数据不一致的问题。例如,在一个电商系统中,商品的价格是外部状态,而商品的类型是内部状态,价格不应该因为商品类型的改变而改变。

6)使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制

划分内部状态和外部状态是享元模式的关键步骤。内部状态是可以共享的部分,而外部状态是需要在使用时传入的部分。合理划分状态可以确保对象的共享性和灵活性。同时,为了实现对象的共享和管理,需要一个工厂类来控制享元对象的创建和获取。工厂类负责维护一个对象池,根据客户端的请求返回相应的享元对象。例如,在一个游戏开发中,对于不同类型的怪物对象,其外观和基本属性可以作为内部状态,而怪物的位置和血量等可以作为外部状态,通过一个怪物工厂类来管理和创建怪物对象。

7)享元模式经典的应用场景是需要缓冲池的场景,比如 String 常量池、数据库连接池

在需要缓冲池的场景中,通常会有大量的对象需要创建和销毁,这会消耗大量的系统资源。享元模式通过共享对象,可以有效地减少对象的创建和销毁次数,提高系统的性能。例如,在 Java 中,String 常量池就是一个典型的享元模式应用。当创建一个字符串常量时,JVM 会先在常量池中查找是否已经存在相同的字符串,如果存在则直接返回该字符串的引用,避免了重复创建相同的字符串对象。同样,数据库连接池也是使用享元模式的思想,将数据库连接对象进行共享,避免了频繁创建和销毁数据库连接,提高了数据库操作的效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qzer_407

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值