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

享元模式是一种结构型设计模式,用于优化大量相似对象的内存使用,通过共享对象减少内存消耗。文章介绍了享元模式的组成部分,如享元、工厂方法、内部状态和外部状态,并讨论了其实现方式,包括单例模式的应用。此外,还提到了享元模式的使用场景和优缺点,并提供了Java代码示例来展示享元模式的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、简介

2、🔺对象类型

2.1、享元之工厂模式

3、组成部分

4、使用场景

5、优缺点

6、实现

6.1、介绍实现方法

6.1.1、单例

6.1.2、常用的单例

6.2、demo代码实现


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 关键字或者静态内部类等方式来解决。

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

public class FlyweightFactory {
    private static FlyweightFactory instance;

    // 私有构造函数
    private FlyweightFactory() {
        // ...
    }

    // 静态方法获取唯一实例
    public static synchronized FlyweightFactory getInstance() {
        if (instance == null) {
            instance = new FlyweightFactory();
        }
        return instance;
    }

    // 其他方法
    // ...
}

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

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

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

public class FlyweightFactory {
    private static volatile FlyweightFactory instance;

    // 私有构造函数
    private FlyweightFactory() {
        // ...
    }

    // 静态方法获取唯一实例
    public static FlyweightFactory getInstance() {
        if (instance == null) {
            synchronized (FlyweightFactory.class) {
                if (instance == null) {
                    instance = new FlyweightFactory();
                }
            }
        }
        return instance;
    }

    // 其他方法
    // ...
}

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

6.2、demo代码实现

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

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

public interface Flyweight {
    void operation(String extrinsicState);
}

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

public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState;
    
    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }
    
    @Override
    public void operation(String extrinsicState) {
        System.out.println("Intrinsic state: " + this.intrinsicState);
        System.out.println("Extrinsic state: " + extrinsicState);
    }
}

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

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

public class FlyweightFactory {
    private Map<String, Flyweight> flyweights = new HashMap<>();
    
    public Flyweight getFlyweight(String intrinsicState) {
        if (!flyweights.containsKey(intrinsicState)) {
            Flyweight flyweight = new ConcreteFlyweight(intrinsicState);
            flyweights.put(intrinsicState, flyweight);
        }
        return flyweights.get(intrinsicState);
    }
}

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

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

public class client {
    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();
        Flyweight flyweight1 = factory.getFlyweight("state1");
        Flyweight flyweight2 = factory.getFlyweight("state1");
        Flyweight flyweight3 = factory.getFlyweight("state2");

        flyweight1.operation("external state 1");
        flyweight2.operation("external state 2");
        flyweight3.operation("external state 3");

    }
}

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

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

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

逐梦苍穹

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

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

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

打赏作者

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

抵扣说明:

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

余额充值