设计模式-结构型模式:享元模式

简介: 设计模式-结构型模式:享元模式

1、简介

享元模式(Flyweight Pattern)是一种结构型设计模式,用于优化大量相似对象的内存使用

它通过共享对象来减少内存使用,从而提高程序的性能和效率。享元模式的核心思想是共享对象,也就是将相同或相似的对象共享一份内存空间。

2、🔺对象类型

在享元模式中,通常有两种对象类型:内部状态和外部状态。

内部状态是可以共享的,而外部状态是不可以共享的

内部状态是指在对象创建时确定并且不会改变的状态,外部状态是指在对象创建之后可能会改变的状态。

通过将相同的内部状态的对象缓存起来,减少了相同内部状态的对象的数量,从而达到减少内存的目的。当需要使用相同内部状态的对象时,只需要从缓存中取出即可。

享元模式的实现需要分离出对象的内部状态和外部状态,然后使用工厂模式来创建和管理对象。

此处为语雀内容卡片,点击链接查看:1、工厂方法 · 语雀

内部状态可以作为享元对象的属性,而外部状态则需要作为方法的参数传入。当需要创建对象时,先检查对象池中是否已经有对应内部状态的对象,如果有则直接返回,如果没有则创建新对象并将其添加到对象池中。在使用对象时,只需要传入外部状态,即可得到完整的对象。

2.1、享元之工厂模式

享元模式使用的是工厂方法模式,而不是抽象工厂模式。

工厂方法模式是一种创建型设计模式,用于定义一个创建对象的接口,但将具体的对象创建延迟到子类中实现。在享元模式中,我们使用了享元工厂类来管理共享对象的创建和缓存,这个工厂类就是工厂方法模式中的工厂。

具体地说,在享元模式中,我们定义了一个 Flyweight 接口,用于定义共享对象的接口;一个具体的享元类 ConcreteFlyweight,用于实现共享对象的具体操作;以及一个享元工厂类 FlyweightFactory,用于管理共享对象的创建和缓存。在享元工厂类中,我们使用工厂方法 getFlyweight 来创建共享对象。

相比之下,抽象工厂模式是一种创建型设计模式,用于定义一系列相关对象的接口,而不需要指定具体的类。在抽象工厂模式中,我们定义了一个抽象工厂类和一组抽象产品类,具体的工厂和产品类是通过实现这些抽象类来实现的。

因此,尽管享元模式和抽象工厂模式都涉及到对象的创建,但它们的目的和实现方式是不同的,因此享元模式使用的是工厂方法模式,而不是抽象工厂模式。

3、组成部分

享元模式包含以下组成部分:

  1. Flyweight(享元):是抽象享元类或者具体享元类的接口,通过这个接口可以接收外部状态并进行相应的处理。
  2. ConcreteFlyweight(具体享元类):是实现 Flyweight 接口的具体类,其中存储了享元的内部状态。
  3. UnsharedConcreteFlyweight(非共享具体享元类):是一些不需要共享的具体享元类,一般情况下很少使用。
  4. FlyweightFactory(享元工厂类):是负责创建和管理享元对象的工厂类,其中维护了一个享元池用于存储已经创建的享元对象,避免重复创建。
  5. Client(客户端):是调用享元对象的客户端代码,可以通过享元工厂获取已经创建的享元对象,并将外部状态传递给享元对象进行处理。

这些组成部分共同协作,实现了对象的共享和复用,从而降低了系统的内存消耗和运行时开销。

4、使用场景

享元模式是一种优化内存使用的设计模式,适用于以下场景:

  1. 对象数量大且相似:如果一个系统中有大量相似的对象需要被创建,而这些对象的内部状态可以共享,那么可以使用享元模式来减少内存的使用,提高程序的性能和效率。比如说,一个文本编辑器中需要创建大量的字符对象,这些字符对象有很多相同的属性,比如字体和颜色,可以使用享元模式来实现。
  2. 内部状态较少:如果一个对象的内部状态较少,那么使用享元模式可以有效地减少内存的使用。比如说,一个围棋游戏中的棋子,它们的内部状态只有颜色和形状,可以使用享元模式来减少内存的使用。
  3. 大对象:如果一个对象很大,而系统中需要创建大量的这种对象,那么可以使用享元模式来减少内存的使用。比如说,一个游戏中需要创建大量的怪物对象,而这些对象都包含了很多属性,可以使用享元模式来优化内存使用。
  4. 对象的状态可以外部化:如果一个对象的状态可以外部化,那么可以使用享元模式来减少内存的使用。比如说,一个网络聊天室中的用户对象,它们的状态可以外部化,比如说用户名、密码、头像等,可以使用享元模式来优化内存使用。
  5. 低延迟要求:如果一个系统需要实现低延迟的操作,那么可以使用享元模式来优化内存使用。由于享元对象已经被缓存起来了,因此可以快速地进行对象的创建和访问,从而实现低延迟的操作。

总之,如果一个系统中有大量相似的对象需要被创建,并且这些对象的内部状态可以共享,那么就可以使用享元模式来优化内存使用,提高程序的性能和效率。

5、优缺点

享元模式是一种优化内存使用的设计模式,它具有以下优点和缺点:

优点:

  1. 减少内存使用:使用享元模式可以减少系统中对象的数量,从而减少内存的使用,提高系统的性能和效率。
  2. 提高性能:由于享元对象已经被缓存起来了,因此可以快速地进行对象的创建和访问,从而提高系统的性能和效率。
  3. 提高可维护性:使用享元模式可以将对象的状态分为内部状态和外部状态,从而提高代码的可维护性。
  4. 可以实现对象池:使用享元模式可以实现对象池,从而实现对象的复用,提高系统的性能和效率。

缺点:

  1. 增加复杂度:使用享元模式需要将对象的状态分为内部状态和外部状态,这增加了代码的复杂度。
  2. 限制状态共享:使用享元模式需要将对象的状态分为内部状态和外部状态,如果对象的状态不能分为内部状态和外部状态,那么就不能使用享元模式。
  3. 破坏封装性:使用享元模式需要将对象的状态分为内部状态和外部状态,这可能会破坏对象的封装性。

总之,享元模式是一种优化内存使用的设计模式,它可以减少内存的使用,提高系统的性能和效率,但也有一些缺点,比如增加代码的复杂度、限制状态共享和破坏对象的封装性。在使用享元模式时,需要根据具体的情况权衡优缺点,选择合适的方案。

6、实现

6.1、介绍实现方法

在Java中,享元模式的实现通常使用了单例模式和哈希表来管理对象池。常见的应用场景包括字符串常量池、线程池和数据库连接池等。通过使用享元模式,可以有效地减少内存使用,提高程序性能和效率。

6.1.1、单例

此处为语雀内容卡片,点击链接查看:3、单例 · 语雀

享元模式的实现可以使用单例模式来保证享元工厂类的唯一性。

享元模式中的享元工厂类负责管理共享对象的创建和缓存,因此必须保证该类的唯一性,以避免创建多个实例而导致的资源浪费和数据不一致等问题。一种常用的实现方式是使用单例模式来实现享元工厂类。

在单例模式中,我们使用一个静态变量来保存该类的唯一实例,同时将构造函数设为私有,以避免外部代码创建新的实例。我们可以将享元工厂类的构造函数设为私有,然后提供一个静态方法来获取该类的唯一实例。这样,无论在何时何地调用该方法,都可以得到同一个享元工厂对象的实例。

6.1.2、常用的单例

(享元模式的实现可以使用任何一种单例模式,比较常用的是懒汉式单例模式和双重检查锁单例模式)

此处为语雀内容卡片,点击链接查看:②懒汉式单例 · 语雀

懒汉式单例模式是一种常用的单例模式,其特点是在首次调用 getInstance 方法时才创建唯一实例。在多线程环境下,懒汉式单例模式需要考虑线程安全性问题,可以通过 synchronized 关键字或者静态内部类等方式来解决。

以下是使用懒汉式单例模式实现享元工厂类的示例代码:

1. public class FlyweightFactory {
2. private static FlyweightFactory instance;
3. 
4. // 私有构造函数
5. private FlyweightFactory() {
6. // ...
7.     }
8. 
9. // 静态方法获取唯一实例
10. public static synchronized FlyweightFactory getInstance() {
11. if (instance == null) {
12.             instance = new FlyweightFactory();
13.         }
14. return instance;
15.     }
16. 
17. // 其他方法
18. // ...
19. }

此处为语雀内容卡片,点击链接查看:③双重检查锁单例 · 语雀

双重检查锁单例模式是一种更加高效的单例模式,其特点是通过双重检查来保证线程安全性和效率。在多线程环境下,双重检查锁单例模式可以避免每次调用 getInstance 方法时都加锁的开销,从而提高了程序的性能。

以下是使用双重检查锁单例模式实现享元工厂类的示例代码:

1. public class FlyweightFactory {
2. private static volatile FlyweightFactory instance;
3. 
4. // 私有构造函数
5. private FlyweightFactory() {
6. // ...
7.     }
8. 
9. // 静态方法获取唯一实例
10. public static FlyweightFactory getInstance() {
11. if (instance == null) {
12. synchronized (FlyweightFactory.class) {
13. if (instance == null) {
14.                     instance = new FlyweightFactory();
15.                 }
16.             }
17.         }
18. return instance;
19.     }
20. 
21. // 其他方法
22. // ...
23. }

在上述代码中,我们使用了 volatile 关键字来保证 instance 变量的可见性,避免出现多线程环境下的数据不一致问题。另外,我们使用了双重检查的方式来避免每次调用 getInstance 方法时都加锁的开销,提高了程序的性能。

6.2、demo代码实现

下面是一个简单的 Java 代码实现享元模式的示例:

首先,我们定义一个接口 Flyweight,用于定义共享对象的接口:

1. public interface Flyweight {
2. void operation(String extrinsicState);
3. }

然后,我们定义一个具体的享元类 ConcreteFlyweight,用于实现共享对象的具体操作:

1. public class ConcreteFlyweight implements Flyweight {
2. private String intrinsicState;
3. 
4. public ConcreteFlyweight(String intrinsicState) {
5. this.intrinsicState = intrinsicState;
6.     }
7. 
8. @Override
9. public void operation(String extrinsicState) {
10.         System.out.println("Intrinsic state: " + this.intrinsicState);
11.         System.out.println("Extrinsic state: " + extrinsicState);
12.     }
13. }

在具体的享元类中,我们定义了一个内部状态 intrinsicState,用于存储共享对象的内部状态。在 operation 方法中,我们输出了共享对象的内部状态和外部状态。

接下来,我们定义一个享元工厂类 FlyweightFactory,用于管理共享对象的创建和缓存:

1. public class FlyweightFactory {
2. private Map<String, Flyweight> flyweights = new HashMap<>();
3. 
4. public Flyweight getFlyweight(String intrinsicState) {
5. if (!flyweights.containsKey(intrinsicState)) {
6. Flyweight flyweight = new ConcreteFlyweight(intrinsicState);
7.             flyweights.put(intrinsicState, flyweight);
8.         }
9. return flyweights.get(intrinsicState);
10.     }
11. }

在享元工厂类中,我们定义了一个 flyweights 字典,用于存储共享对象。在 getFlyweight 方法中,我们首先判断字典中是否已经存在对应的共享对象,如果不存在,则创建一个新的共享对象并加入字典,如果存在,则直接返回字典中的共享对象。

最后,我们可以使用如下代码来测试享元模式的实现:

1. public class client {
2. public static void main(String[] args) {
3. FlyweightFactory factory = new FlyweightFactory();
4. Flyweight flyweight1 = factory.getFlyweight("state1");
5. Flyweight flyweight2 = factory.getFlyweight("state1");
6. Flyweight flyweight3 = factory.getFlyweight("state2");
7. 
8.         flyweight1.operation("external state 1");
9.         flyweight2.operation("external state 2");
10.         flyweight3.operation("external state 3");
11. 
12.     }
13. }

在测试代码中,我们首先创建了一个享元工厂类的实例 factory,然后使用 factory.getFlyweight 方法来获取共享对象。我们创建了两个状态相同的共享对象 flyweight1 和 flyweight2,以及一个状态不同的共享对象 flyweight3。最后,我们分别调用了这三个共享对象的 operation 方法,并传递了不同的外部状态。

当我们运行这段代码时,可以看到如下输出结果:

从输出结果可以看出,享元模式可以有效地共享对象,减少内存的使用,提高系统的性能和效率。

 

相关文章
|
9月前
|
设计模式 存储 缓存
【设计模式】【结构型模式】享元模式(Flyweight)
一、入门 什么是享元模式? 享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用,特别适用于存在大量相似对象的情况。 它的核心思想是将对象的内在状态(不变
317 16
|
9月前
|
设计模式 缓存 安全
【高薪程序员必看】万字长文拆解Java并发编程!(8):设计模式-享元模式设计指南
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
201 0
|
设计模式 存储 缓存
「全网最细 + 实战源码案例」设计模式——享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,旨在减少大量相似对象的内存消耗。通过分离对象的内部状态(可共享、不变)和外部状态(依赖环境、变化),它有效减少了内存使用。适用于存在大量相似对象且需节省内存的场景。模式优点包括节省内存和提高性能,但会增加系统复杂性。实现时需将对象成员变量拆分为内在和外在状态,并通过工厂类管理享元对象。
425 92
|
9月前
|
设计模式 Java 数据库连接
【设计模式】【结构型模式】代理模式(Proxy)
一、入门 什么是代理模式? 代理模式(Proxy Pattern)是一种结构型设计模式,允许你提供一个代理对象来控制对另一个对象的访问。 代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对
274 10
|
9月前
|
设计模式 Java 定位技术
【设计模式】【结构型模式】组合模式(Composite)
一、入门 什么是组合模式 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得客户端可以统一处理单个对象和组合对
296 10
|
9月前
|
关系型数据库 Java MySQL
【设计模式】【结构型模式】桥接模式(Bridge)
一、入门 什么是桥接模式? 桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离,让它们可以独立变化。简单来说,它像一座“桥”连接了两个维度的变化,避免用继承导致代
471 10
|
9月前
|
设计模式 前端开发 Java
【设计模式】【结构型模式】适配器模式(Adpter)
一、入门 什么是适配器模式? 适配器模式是Java中常用的结构型设计模式,它的核心作用就像现实中的电源转换器一样---让原本不兼容的两个接口能够协同工作。 为什么要用适配器模式? 假设我们需要在电商系
240 10
|
9月前
|
设计模式 Java 数据库连接
【设计模式】【结构型模式】外观模式(Facde)
一、入门 什么是外观模式? 一种结构型设计模式,通过为子系统中的一组接口提供一个统一的高层接口(称为外观),来简化客户端与复杂子系统的交互过程。其本质是建立抽象层来隔离复杂度。 为什么要有外观模式?
365 9
|
9月前
|
设计模式 缓存 安全
【设计模式】【结构型模式】装饰者模式(Decorator)
一、入门 什么是装饰者模式? 装饰者模式(Decorator Pattern)是 Java 中常用的结构型设计模式,它能在不修改原有对象结构的前提下,动态地为对象添加额外的职责。 为什么要装饰者模式?
236 8
|
设计模式 存储 Java
【九】设计模式~~~结构型模式~~~外观模式(Java)
文章详细介绍了外观模式(Facade Pattern),这是一种对象结构型模式,通过引入一个外观类来简化客户端与多个子系统之间的交互,降低系统的耦合度,并提供一个统一的高层接口来使用子系统。通过文件加密模块的实例,展示了外观模式的动机、定义、结构、优点、缺点以及适用场景,并讨论了如何通过引入抽象外观类来提高系统的可扩展性。
【九】设计模式~~~结构型模式~~~外观模式(Java)