23种设计模式——享元模式(Flyweight Pattern)

✅作者简介:大家好,我是 Meteors., 向往着更加简洁高效的代码写法与编程方式,持续分享Java技术内容。
🍎个人主页:Meteors.的博客

💞当前专栏:设计模式
✨特色专栏:知识分享
🥭本文内容:23种设计模式------享元模式(Flyweight Pattern)
📚 ** ps ** :阅读文章如果有问题或者疑惑,欢迎在评论区提问或指出。


目录

[一. 背景](#一. 背景)

[二. 介绍](#二. 介绍)

[三. 主要角色](#三. 主要角色)

[四. 核心思想](#四. 核心思想)

[五. 应用场景](#五. 应用场景)

[六. 具体案例](#六. 具体案例)

[七. 总结](#七. 总结)


一. 背景

享元模式,相信很多人都会对它有点陌生。和它的命名类似,它本质上其实就是把已创建的元素分享出去,避免相同的元素过多。和内存的缓存类似,如果说Redis缓存是在分布式环境、服务之间共享。那么享元模式就是*进程内、对象级别共享。***下面会对享元模式进行详细的介绍。


二. 介绍

享元模式(Flyweight Pattern)是23种设计模式中的一种结构型模式,主要用于减少创建对象的数量,以减少内存占用和提高性能。

享元模式的核心思想是通过共享技术来有效地支持大量细粒度对象的复用。它将对象的内部状态和外部状态区分开来,内部状态是对象共享的部分,外部状态是对象独立的部分。

当系统中存在大量相似或完全相同的对象,且这些对象消耗大量内存时,享元模式能够通过共享相同的部分来显著减少内存使用


三. 主要角色

Flyweight(抽象享元类): 定义对象的接口,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。
**ConcreteFlyweight(具体享元类):**实现抽象享元角色所规定的接口。如果有内部状态,可以在类内部定义。
FlyweightFactory(享元工厂类): 负责创建和管理享元角色。当客户端请求一个具体享元角色时,享元工厂角色需要检查系统中是否已有一个符合要求的享元角色。如果已经有了,享元工厂角色就应当提供这个已有的享元角色;如果没有,则创建一个合适的享元角色。
**Client(客户端):**维护对所有享元对象的引用,而且还需要存储对应的外部状态。


四. 核心思想

享元模式的关键是区分内部状态和外部状态:
内部状态(Intrinsic State): 在享元对象内部不随外界环境改变而改变的共享部分。
**外部状态(Extrinsic State):**随环境改变而改变的、不可以共享的状态。


五. 应用场景

在下面场景中,可以考虑使用享元模式:

  1. 系统中存在大量相似对象
  2. 对象创建和存储成本较高
  3. 对象可以轻松共享而不影响其外部行为
  4. 系统不依赖于这些对象的身份,这些对象是不可分辨的

具体的场景中,在下面情况下,我们可以考虑使用享元模式:

  1. 文本编辑器中的字符对象:在处理大量文本时,每个字符可以作为享元对象,字符的字体、大小等作为内部状态,字符在文档中的位置作为外部状态。
  2. 游戏开发中的粒子系统:在游戏特效中,大量的粒子可以共享纹理、模型等内部状态,而位置、速度等作为外部状态。
  3. Java中的String常量池:Java的String常量池就是享元模式的应用,相同内容的字符串在内存中只保存一份。

六. 具体案例

下面是一个使用享元模式实现画图的demo,内部状态就是形状的颜色,外部状态是形状的x/y的坐标:

java 复制代码
import java.util.HashMap;
import java.util.Map;

// 抽象享元类
interface Shape {
    void draw(int x, int y);
}

// 具体享元类
class Circle implements Shape {
    // 内在状态 - 可以共享的属性
    private String color;
    
    public Circle(String color) {
        this.color = color;
        System.out.println("创建新的圆形: " + color);
    }
    
    // 外在状态 - 随环境变化,不可共享的属性,由客户端传入
    @Override
    public void draw(int x, int y) {
        System.out.println("绘制圆形 - 颜色: " + color + ", 坐标: (" + x + "," + y + ")");
    }
}

// 享元工厂类
class ShapeFactory {
    private static final Map<String, Shape> shapeMap = new HashMap<>();
    
    public static Shape getCircle(String color) {
        Shape circle = shapeMap.get(color);
        
        if (circle == null) {
            circle = new Circle(color);
            shapeMap.put(color, circle);
            System.out.println("创建新的圆形并放入缓存中: " + color);
        } else {
            System.out.println("复用缓存中的圆形: " + color);
        }
        
        return circle;
    }
    
    public static int getShapeCount() {
        return shapeMap.size();
    }
}

// 客户端代码
public class FlyweightPatternDemo {
    private static final String[] colors = {"红色", "绿色", "蓝色", "黄色", "黑色"};
    
    public static void main(String[] args) {
        // 创建大量圆形对象
        for (int i = 0; i < 20; ++i) {
            Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
            circle.draw(getRandomX(), getRandomY());
        }
        
        System.out.println("总共创建的圆形对象数量: " + ShapeFactory.getShapeCount());
    }
    
    private static String getRandomColor() {
        return colors[(int) (Math.random() * colors.length)];
    }
    
    private static int getRandomX() {
        return (int) (Math.random() * 100);
    }
    
    private static int getRandomY() {
        return (int) (Math.random() * 100);
    }
}

七. 总结

通俗的说,享元模式通过共享技术来高效地支持大量细粒度对象的复用,以减少内存使用和提高性能。当我们有多个相似对象,且有部分相同属性时,可以考虑使用享元模式,减少对象数量,共享相同对象以节省内存。

相关推荐
数据知道8 小时前
Go语言设计模式:抽象工厂模式详解
设计模式·golang·抽象工厂模式·go语言
数据知道8 小时前
Go语言设计模式:组合模式详解
设计模式·golang·组合模式·go语言
有意义10 小时前
Spring Boot 项目中部门查询功能实现与依赖注入优化
后端·设计模式
岁忧13 小时前
Go channel 的核心概念、操作语义、设计模式和实践要点
网络·设计模式·golang
songgeb15 小时前
《设计模式之美》之适配器模式
设计模式
Yeniden15 小时前
【设计模式】享元模式(Flyweight)大白话讲解!
java·设计模式·享元模式
乙己40715 小时前
设计模式——单例模式(singleton)
java·c++·单例模式·设计模式
这不小天嘛16 小时前
23 种经典设计模式的名称、意图及适用场景概述
设计模式
数据知道1 天前
Go语言设计模式:适配器模式详解
设计模式·golang·建造者模式
执笔论英雄1 天前
【设计模式】策略类和依赖注入
设计模式