十一、结构型(享元模式)

devtools/2024/10/18 11:46:42/

享元模式(Flyweight Pattern)

概念
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享大量细粒度对象来减少内存占用。享元模式将内外部状态分离,内部状态可以共享,外部状态由客户端独立维护,从而避免重复创建相同内容的对象。


应用场景

  1. 需要大量相似对象且内存消耗较大:当系统需要生成大量相似的对象,而这些对象中的大部分内容是相同或可以共享的,通过享元模式可以减少内存开销。例如,绘图程序中的文字对象、图形对象等。

  2. 对象的部分状态是可共享的:如果一个对象的某些部分是可变的(外部状态),而其他部分是不可变的(内部状态),那么可以使用享元模式,将内部状态共享,外部状态由客户端独立处理。例如,在棋类游戏中,棋子的颜色和形状是共享的,而位置则是独立的。

  3. 系统中创建了大量细粒度对象:当系统中需要创建大量的细粒度对象时,可以使用享元模式来减少对象的数量,从而节省资源。


注意点

  • 区分内外部状态:内部状态是对象可以共享的部分,外部状态是依赖于具体场景并且不可共享的部分。外部状态在使用时会被传递给享元对象。
  • 使用享元工厂管理享元对象:由于共享对象的核心是避免重复创建,工厂模式通常与享元模式配合使用,用来管理享元对象的创建和共享。
  • 节省内存的代价是增加了系统的复杂性享元模式虽然可以减少对象的数量,但增加了状态管理的复杂性,外部状态需要由客户端管理。

核心要素

  1. Flyweight(享元接口):定义享元对象接口,通常包含操作内部状态的方法。
  2. ConcreteFlyweight(具体享元类):实现享元接口,封装可共享的内部状态。
  3. UnsharedConcreteFlyweight(非共享享元类):有时享元对象的某些实例不能共享,此时可以使用这个类表示非共享的对象。
  4. FlyweightFactory(享元工厂类):负责管理和创建享元对象,确保对象可以被共享。

Java代码完整示例

// 享元接口
interface Flyweight {void operation(String externalState);  // 接受外部状态
}// 具体享元类,保存共享的内部状态
class ConcreteFlyweight implements Flyweight {private String internalState;  // 内部状态public ConcreteFlyweight(String internalState) {this.internalState = internalState;}@Overridepublic void operation(String externalState) {System.out.println("内部状态: " + internalState + ", 外部状态: " + externalState);}
}// 享元工厂类,管理享元对象
class FlyweightFactory {private Map<String, Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String internalState) {// 检查享元对象是否已经存在if (!flyweights.containsKey(internalState)) {flyweights.put(internalState, new ConcreteFlyweight(internalState));System.out.println("创建享元对象,内部状态: " + internalState);}return flyweights.get(internalState);}public int getFlyweightCount() {return flyweights.size();}
}// 客户端代码
public class Client {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();// 创建享元对象并共享内部状态Flyweight flyweight1 = factory.getFlyweight("A");flyweight1.operation("X");Flyweight flyweight2 = factory.getFlyweight("B");flyweight2.operation("Y");Flyweight flyweight3 = factory.getFlyweight("A");flyweight3.operation("Z");System.out.println("享元对象的数量: " + factory.getFlyweightCount());}
}

输出结果

创建享元对象,内部状态: A
内部状态: A, 外部状态: X
创建享元对象,内部状态: B
内部状态: B, 外部状态: Y
内部状态: A, 外部状态: Z
享元对象的数量: 2

各种变形用法完整示例

  1. 非共享的享元对象
    有时某些享元对象不能共享,需要为这些对象设计一个独立的类,这类对象称为“非共享享元对象”。例如,在某些系统中,特殊状态的对象不能被共享。

    代码示例

    // 非共享享元类
    class UnsharedConcreteFlyweight implements Flyweight {private String allState;public UnsharedConcreteFlyweight(String allState) {this.allState = allState;}@Overridepublic void operation(String externalState) {System.out.println("非共享对象状态: " + allState + ", 外部状态: " + externalState);}
    }public class ClientWithUnsharedFlyweight {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight flyweight1 = factory.getFlyweight("A");flyweight1.operation("X");Flyweight unsharedFlyweight = new UnsharedConcreteFlyweight("Special");unsharedFlyweight.operation("Y");System.out.println("享元对象的数量: " + factory.getFlyweightCount());}
    }
    
  2. 复合享元模式
    复合享元模式是指将多个享元对象组合起来形成一个更大的享元对象,外部状态被管理成一个组合结构。这个组合结构可以递归地包含多个享元对象。

    代码示例

    // 复合享元类
    class CompositeFlyweight implements Flyweight {private Map<String, Flyweight> flyweightMap = new HashMap<>();public void add(String key, Flyweight flyweight) {flyweightMap.put(key, flyweight);}@Overridepublic void operation(String externalState) {for (Map.Entry<String, Flyweight> entry : flyweightMap.entrySet()) {entry.getValue().operation(externalState);}}
    }public class ClientCompositeFlyweight {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();// 创建单个享元对象Flyweight flyweightA = factory.getFlyweight("A");Flyweight flyweightB = factory.getFlyweight("B");// 创建复合享元对象CompositeFlyweight compositeFlyweight = new CompositeFlyweight();compositeFlyweight.add("A", flyweightA);compositeFlyweight.add("B", flyweightB);// 使用复合享元compositeFlyweight.operation("CompositeState");System.out.println("享元对象的数量: " + factory.getFlyweightCount());}
    }
    
  3. 与工厂模式结合
    享元模式通常与工厂模式结合使用,由工厂类负责管理和创建享元对象,确保对象可以被重复使用。工厂类通常通过缓存已创建的对象来避免重复创建。

    代码示例

    // 在前面的 FlyweightFactory 中已经使用了工厂模式
    
  4. 享元模式在Java标准库中的应用
    在Java的标准库中,Integer.valueOf()方法就是享元模式的一个实际应用。为了减少内存消耗,JDK对-128127之间的整数进行了缓存。

    代码示例

    public class FlyweightInJava {public static void main(String[] args) {Integer int1 = Integer.valueOf(100);Integer int2 = Integer.valueOf(100);// 比较对象引用System.out.println(int1 == int2);  // trueInteger int3 = Integer.valueOf(200);Integer int4 = Integer.valueOf(200);System.out.println(int3 == int4);  // false}
    }
    

总结
享元模式是一种优化内存使用的有效方式,尤其适用于需要创建大量细粒度对象的系统。通过共享内部状态,享元模式可以减少重复对象的创建,从而降低内存开销。如果系统中对象数量巨大且状态重复较多,享元模式可以显著提升性能和内存使用效率。


http://www.ppmy.cn/devtools/126712.html

相关文章

数据字典是什么?和数据库、数据仓库有什么关系?

一、数据字典的定义及作用 数据字典是一种对数据的定义和描述的集合&#xff0c;它包含了数据的名称、类型、长度、取值范围、业务含义、数据来源等详细信息。 数据字典的主要作用如下&#xff1a; 1. 对于数据开发者来说&#xff0c;数据字典包含了关于数据结构和内容的清晰…

FreeRTOS - 队列

在学习FreeRTOS过程中&#xff0c;结合韦东山-FreeRTOS手册和视频、野火-FreeRTOS内核实现与应用开发、及网上查找的其他资源&#xff0c;整理了该篇文章。如有内容理解不正确之处&#xff0c;欢迎大家指出&#xff0c;共同进步。 1. 队列 1.1 队列基本概念 队列(queue)可以用…

UNIX网络编程-简介

概述 要编写通过计算机网络通信的程序&#xff0c;首先要确定这些程序相互通信所用的协议&#xff08;protocal&#xff09;。 大多数网络应用划分为客户端&#xff08;client&#xff09;和服务器&#xff08;server&#xff09;。在设计网络应用时&#xff0c;确定总是由客户…

线性代数 行列式

一、行列式 1、定义 一个数学概念&#xff0c;主要用于 线性代数中&#xff0c;它是一个可以从方阵&#xff08;即行数和列数相等的矩阵&#xff09;形成的一个标量&#xff08;即一个单一的数值&#xff09; 2、二阶行列式 &#xff0c;像这样将一个式子收缩称为一个 2*2 的…

2d 数字人实时语音聊天对话使用案例;支持asr、llm、tts实时语音交互

参考: https://github.com/lyz1810/live2dSpeek 下载live2dSpeek项目 ## 下载live2dSpeek git clone https://github.com/lyz1810/live2dSpeek cd live2dSpeek-main ## 运行live2dSpeek npm install -g http-server http-server .更改新的index.html页面 index.html

Android从上帝视角来看PackageManagerService

戳蓝字“牛晓伟”关注我哦&#xff01; 用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章&#xff0c;技术文章也可以有温度。 前言 阅读该篇之前&#xff0c;建议先阅读下面的系列文章&#xff1a; Android深入理解包管理–PackageManagerService和它的“小伙伴…

《拿下奇怪的前端报错》:1比特丢失导致的音视频播放时长无限增长-浅析http分片传输核心和一个坑点

问题背景 在一个使用MongoDB GridFS实现文件存储和分片读取的项目中&#xff0c;同事遇到了一个令人困惑的问题&#xff1a;音频文件总是丢失最后几秒&#xff0c;视频文件也出现类似情况。更奇怪的是&#xff0c;播放器显示的总时长为无限大。这个问题困扰了团队成员几天&…

【RS】GEE(Python):栅格计算

在遥感影像处理中&#xff0c;栅格计算是一项至关重要的操作。栅格数据代表了地球表面特定范围内的物理量信息&#xff0c;利用栅格计算可以进行多种分析操作&#xff0c;比如计算植被指数、分类、过滤、组合波段&#xff0c;甚至执行复杂的空间分析任务。本篇教程将详细介绍遥…