大数据知识体系
首页
数据结构与算法
  • JVM
  • Java
  • Scala
  • Python
设计模式
  • MySQL
  • Redis
  • HDFS
  • HBase
  • ClickHouse
  • ElasticSearch
  • Iceberg
  • Hudi
  • Spark
  • Flink
  • Hive
  • Yarn
  • Zookeeper
  • Maven
  • Git
  • 数据仓库
  • 用户画像
  • 指标体系
数据治理
关于
首页
数据结构与算法
  • JVM
  • Java
  • Scala
  • Python
设计模式
  • MySQL
  • Redis
  • HDFS
  • HBase
  • ClickHouse
  • ElasticSearch
  • Iceberg
  • Hudi
  • Spark
  • Flink
  • Hive
  • Yarn
  • Zookeeper
  • Maven
  • Git
  • 数据仓库
  • 用户画像
  • 指标体系
数据治理
关于
  • 设计模式概述
  • 创建型模式

    • 单例模式
    • 简单工厂模式
    • 工厂方法模式
    • 抽象工厂模式
    • 建造者模式
    • 原型模式
  • 结构型模式

    • 适配器模式
    • 装饰器模式
    • 代理模式
    • 外观模式
    • 桥接模式
    • 组合模式
    • 享元模式
      • 一、概述
        • 1.1 解决了什么问题
        • 1.2 解决方案
      • 二、实现方式
        • 2.1 角色
        • 2.2 代码
      • 三、源码中的应用
  • 行为型模式

    • 策略模式
    • 模板方法模式
    • 观察者模式
    • 迭代器模式
    • 责任链模式
    • 命令模式
    • 备忘录模式
    • 状态模式
    • 访问者模式
    • 中介者模式
    • 解释器模式
  • 设计模式
  • 结构型模式
Will
2022-03-29
目录

享元模式

# 一、概述

享元(Flyweight)模式摒弃了在每个对象中保存所有数据的方式,通过共享多个对象共有的相同状态,使得能在有限的内存中加载更多对象。

享元对象的核心是系统中会产生大量的同类对象,通过共享对象内在状态,不需要总是创建新对象,从而降低内存占用并提高效率。正因为这样,所以享元模式在池化技术中得到了广泛应用。

# 1.1 解决了什么问题

在坦克大战游戏中会产生大量的子弹,每个子弹都可以被认为是一个对象。和子弹类似的还有坦克对象、墙体对象等等,在一局十几分钟的游戏中,可能会产生许许多多的子弹对象、坦克对象、墙体对象等。如果每个对象都是被独立 new 出来的,那么每个子弹对象、坦克对象、墙体对象都需要加载自己对应的矢量图以及其它一些属性信息,这样会造成很大的空间浪费,甚至对于一些配置较低的机器会造成卡顿,严重影响游戏体验。

实际上有些对象的属性是可以重复利用的,而不需要每次都重新加载。就拿子弹对象来说,同一类型的子弹(相同型号坦克发射的子弹)的矢量图、子弹大小、颜色、杀伤力、移动速度都是一样的,如果对这些相同的属性只创建一个实例对象重复利用,是不是可以节省很多内存呢?

# 1.2 解决方案

对象中的常量属性通常被称为内在状态,因为这些属性在对象外部是不能改变的,而那些允许在对象外部改变的属性被称为外在状态。

在坦克大战这个案例中,子弹的矢量图、子弹大小、颜色、杀伤力可以被认为是内在状态,而子弹的方向、和初始化的坐标,可以认为是外在状态。如果能够将对象的内在状态抽象出来统一存储进行共享,那么外在状态在需要的时候及时创建就可以满足需求,将这种只存储内在状态的对象称为享元。

在享元模式中,内在状态各个对象之间共享,外部状态由客户端传入。

# 二、实现方式

# 2.1 角色

  1. Flyweight:享元接口,定义所有对象的共享操作。
  2. ConcreteFlyweight:具体的享元角色,实现享元接口,是被共享的部分,其属性为内在状态。
  3. UnSharedConcreteFlyweight(非必须):同样实现享元接口,当不需要共享对象时,但又需要以统一的接口处理此对象,可以添加此角色。
  4. FlyweightFactory:享元工厂类,构建一个共享容器池和从池中获取对象的方法,为客户端提供共享对象。

# 2.2 代码

定义 Flyweight 接口:

public interface Bullet {
    void draw(int x, int y);
}
1
2
3

定义享元角色 ConcreteFlyweight,该对象内部的属性均为内在状态,是共享的部分:

import java.awt.*;

public class GeneralBullet implements Bullet {

    private final String name = "普通炮弹";

    // 为了简化,矢量图用字符串替代
    private final String img = "xxx";

    private final int size = 50;

    private final Color color = Color.BLACK;

    private final int lethality = 100;

    @Override
    public void draw(int x, int y) {
        System.out.println(String.format("普通炮弹位置:%d, %d", x, y));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.awt.*;

public class SuperBullet implements Bullet {

    private final String name = "超级炮弹";

    // 为了简化,矢量图用字符串替代
    private final String img = "yyy";

    private final int size = 100;

    private final Color color = Color.YELLOW;

    private final int lethality = 200;

    @Override
    public void draw(int x, int y) {
        System.out.println(String.format("超级炮弹位置:%d, %d", x, y));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

定义 FlyweightFactory,达到共享的效果:

public class BulletFactory {
    private static final Map<String, Bullet> map = new HashMap<>();

    public static Bullet getBullet(String name) {
        Bullet bullet = map.get(name);

        if (bullet == null) {
            if (name.equals("普通炮弹")) {
                bullet = new GeneralBullet();
            } else if (name.equals("超级炮弹")) {
                bullet = new SuperBullet();
            } else {
                throw new IllegalArgumentException();
            }

            map.put(name, bullet);
        }

        return bullet;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

客户端:

public class FlyweightTest {
    public static void main(String[] args) {

        Set<Integer> generalSet1 = new HashSet<>();
        Set<Integer> generalSet2 = new HashSet<>();

        // 通过传统的 new 方式创建10000个对象
        for (int i = 0; i < 10000; i++) {
            GeneralBullet bullet = new GeneralBullet();
            bullet.draw(i, i + 1);
            generalSet1.add(bullet.hashCode());
        }

        // 通过 FlyweightFactory 获取10000个对象
        for (int i = 0; i < 10000; i++) {
            Bullet bullet = BulletFactory.getBullet("普通炮弹");
            bullet.draw(i, i + 1);
            generalSet2.add(bullet.hashCode());
        }

        System.out.println(generalSet1.size());
        System.out.println(generalSet2.size());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
......
10000
1
1
2
3

# 三、源码中的应用

  1. java.lang.Integer#valueOf(int)
  2. java.lang.Boolean#valueOf(boolean)
  3. java.lang.Byte#valueOf(byte)
  4. java.lang.Character#valueOf(char)
上次更新: 2023/11/01, 03:11:44

← 组合模式 策略模式→

Theme by Vdoing | Copyright © 2022-2023 Will 蜀ICP备2022002285号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式