软件设计模式系列之十三——享元模式

1 模式的定义

享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在减少内存占用或计算开销,通过共享大量细粒度对象来提高系统的性能。这种模式适用于存在大量相似对象实例,但它们的状态可以外部化(extrinsic),并且可以在多个对象之间共享的情况。

2 举例说明

为了更好地理解享元模式,让我们举一些现实生活中的例子。

咖啡店的咖啡杯和碟子的例子。在咖啡店中,咖啡杯和碟子通常具有相同的设计和形状,但它们可能具有不同的颜色或图案。咖啡店可以使用享元模式来共享相同设计的杯子和碟子,以减少存储和管理的成本。
在这里插入图片描述

公共交通卡的例子。城市中的公共交通卡(如地铁卡、公共汽车卡)通常具有相同的功能和外观,但每张卡可能包含不同的余额和个人信息。这些卡可以被视为享元对象,公共交通系统可以共享卡的通用功能。

电子书阅读器的字体和样式的例子。电子书阅读器可以使用享元模式来管理字体、字号和样式。多本电子书可以共享相同的字体和样式设置,以提供一致的阅读体验。

这些例子都涉及到具有相似属性和功能的对象,它们可以通过享元模式来共享通用部分,从而减少资源消耗并提高效率。这在设计和生产中可以节省时间和成本。

3 结构

享元模式的结构包括以下主要组件:
在这里插入图片描述

享元工厂(Flyweight Factory):享元工厂负责创建和管理享元对象。它维护一个享元池,其中包含已经创建的享元对象,并根据客户端请求共享已经存在的对象或创建新的享元对象。

享元接口(Flyweight Interface):享元接口是享元对象的抽象,通常声明了享元对象的公共方法,以便客户端能够访问和操作享元对象。

具体享元(Concrete Flyweight):具体享元是享元接口的实现,包含了内部状态和外部状态。内部状态是可以被共享的,而外部状态是不可共享的,它在运行时传递给享元对象。

客户端(Client):客户端是使用享元模式的应用程序或模块,它通过享元工厂来获取或共享享元对象,并根据需要传递外部状态。

4 实现步骤

要实现享元模式,可以按照以下步骤进行操作:

确定内部状态和外部状态:首先,确定对象的内部状态和外部状态。内部状态是可以被多个对象共享的部分,而外部状态是不可共享的。

创建享元接口:定义享元接口,声明享元对象的公共方法,包括操作内部状态和外部状态的方法。

创建具体享元类:实现具体享元类,它包含了内部状态和外部状态的具体实现。内部状态可以在多个对象之间共享,而外部状态需要在运行时传递。

创建享元工厂:创建享元工厂,负责创建和管理享元对象。享元工厂可以维护一个享元池,用于存储已经创建的享元对象。

客户端使用享元对象:在客户端中,通过享元工厂来获取或共享享元对象。客户端需要提供外部状态作为参数,并根据需要操作享元对象。

5 代码实现

以下是一个简单的 Java 代码示例,演示了如何使用享元模式来实现公共交通卡的共享功能。在这个示例中,我们创建了一个 TransportCardFactory 工厂类来管理交通卡对象,以及一个 TransportCard 接口表示交通卡。

// 1. 定义交通卡接口
interface TransportCard {void swipe();
}// 2. 创建具体的交通卡类
class SubwayCard implements TransportCard {private String ownerName;private int balance;public SubwayCard(String ownerName) {this.ownerName = ownerName;this.balance = 0;}public void swipe() {System.out.println("刷地铁卡,扣除票价,余额:" + balance);}
}// 3. 创建享元工厂类
class TransportCardFactory {private Map<String, TransportCard> cards = new HashMap<>();public TransportCard getCard(String ownerName) {if (cards.containsKey(ownerName)) {System.out.println("使用现有的交通卡:" + ownerName);return cards.get(ownerName);} else {System.out.println("创建新的交通卡:" + ownerName);TransportCard card = new SubwayCard(ownerName);cards.put(ownerName, card);return card;}}
}// 4. 客户端代码
public class Client {public static void main(String[] args) {TransportCardFactory cardFactory = new TransportCardFactory();// 乘客1刷卡TransportCard card1 = cardFactory.getCard("zhanngsan");card1.swipe();// 乘客2刷卡TransportCard card2 = cardFactory.getCard("lisi");card2.swipe();// 再次刷卡TransportCard card3 = cardFactory.getCard("zhanngsan");card3.swipe();}
}

在这个示例中,我们首先定义了 TransportCard 接口,表示交通卡的通用功能。然后,我们创建了一个具体的交通卡类 SubwayCard,它实现了 TransportCard 接口,并包含了特定于地铁卡的属性。

接下来,我们创建了享元工厂类 TransportCardFactory,它负责管理和共享交通卡对象。当客户端需要一个交通卡时,工厂类会首先检查是否已经存在具有相同拥有者姓名的卡,如果存在则返回现有的卡,否则创建一个新的卡对象。

最后,我们在客户端代码中演示了如何使用享元模式,创建并刷卡,观察到当两位乘客使用相同姓名刷卡时,会共享同一个交通卡对象,从而减少了卡对象的创建和内存占用。

6 典型应用场景

6.1 享元模式通常在以下情况下得到广泛应用

在这里插入图片描述

  • 大量对象。当系统中存在大量相似对象实例时,使用享元模式可以显著减少内存占用,因为相似对象的内部状态可以共享。

  • 内部状态与外部状态。当对象可以分为内部状态和外部状态时,享元模式特别有用。内部状态是对象的固定部分,可以被多个对象共享,而外部状态是对象的可变部分,每个对象可以根据需要个性化。

  • 性能优化。在需要高性能和低内存消耗的情况下,享元模式可以用于共享重复使用的对象,从而提高系统的性能。

  • 缓存管理。在需要缓存大量对象以提高系统响应时间的情况下,可以使用享元模式来管理缓存对象。

  • 资源池管理。当需要管理共享资源池(如数据库连接池、线程池)中的资源对象时,享元模式可以用于有效地共享和重用资源。

享元模式在需要管理大量相似对象、共享内部状态、提高性能和减少内存占用的情况下非常有用。它允许对象在不同上下文中共享内部状态,而外部状态可以根据需要进行个性化定制。通过合理使用享元模式,可以改善系统的效率和资源利用率。

6.2 java中的字符串应用享元模式场景

在Java中,字符串是使用享元模式的经典示例。享元模式的核心思想是共享相似对象的内部状态,以减少内存占用。字符串的使用正是基于这个思想。

下面是Java中字符串如何使用享元模式的一些关键特点:

不可变性:Java中的字符串是不可变的,也就是说一旦创建了一个字符串对象,它的值就不能被修改。这意味着如果两个字符串具有相同的字符序列,它们可以共享相同的内部字符数组。

字符串常量池:Java维护了一个字符串常量池(String Pool),用于存储字符串字面量。当你创建一个字符串字面量时,Java会首先检查常量池中是否已经存在相同值的字符串。如果存在,它将返回常量池中的字符串引用,而不会创建新的对象。

共享相同的字符串对象:由于字符串的不可变性和字符串常量池的存在,多个字符串变量可以共享相同的字符串对象。这意味着如果你有多个字符串变量引用相同的字符串值,它们实际上共享同一个字符串对象。

下面是一个示例,演示了字符串如何使用享元模式:

String s1 = "Hello"; // 创建一个字符串字面量,存储在常量池中
String s2 = "Hello"; // 与s1共享相同的字符串对象String s3 = new String("Hello"); // 创建一个新的字符串对象,不存储在常量池中
String s4 = new String("Hello"); // 创建另一个新的字符串对象,也不存储在常量池中System.out.println(s1 == s2); // true,s1和s2共享相同的字符串对象
System.out.println(s1 == s3); // false,s1和s3引用不同的字符串对象

在上面的示例中,s1 和 s2 共享相同的字符串对象,因为它们引用相同的字符串字面量,而 s3 和 s4 创建了新的字符串对象,因为它们使用了 new 操作符。这种共享内部状态的方式减少了内存占用,并提高了性能,特别是当处理大量字符串时。

Java中的字符串是一个典型的享元模式的例子,通过不可变性和字符串常量池,它实现了字符串对象的共享,以减少内存占用和提高性能。这种设计对于处理字符串操作非常高效,并且保证了字符串值的安全性,因为它们不可被修改。

7 优缺点

享元模式具有一些优点和缺点,让我们来看看:

优点:

减少内存占用,享元模式通过共享相似对象的内部状态,可以大大减少内存占用,提高系统的性能和效率。提高性能,通过共享对象,减少了对象的创建和销毁,从而提高了系统的性能。分离内部状态和外部状态,享元模式允许将内部状态和外部状态分开,外部状态可以在运行时传递给享元对象,使系统更灵活。

缺点:

增加复杂性,享元模式引入了共享对象和外部状态的概念,可能增加了系统的复杂性。可能导致线程安全问题,如果多个线程同时访问共享对象并修改其外部状态,可能会导致线程安全问题。

8 类似模式

享元模式通常与其他设计模式一起使用,以解决更复杂的问题或实现更全面的系统。以下是一些常见的设计模式,以及如何与享元模式一起使用它们。

工厂模式。享元模式通常需要一个工厂来创建和管理共享对象。你可以使用工厂模式来创建享元对象,确保对象的创建和初始化过程是封装的,并且客户端不需要直接创建对象。

单例模式。在享元模式中,享元工厂可以是一个单例,以确保只有一个享元工厂实例用于管理共享对象。这样可以确保对象的唯一性和一致性。

装饰者模式:装饰者模式可以与享元模式一起使用,以动态地添加功能或状态到享元对象。装饰者模式允许你在不改变对象结构的情况下,为对象添加额外的行为。

代理模式:代理模式可以与享元模式一起使用,以提供对享元对象的访问控制或延迟加载。代理可以用于监控或限制对共享对象的访问。

组合模式:组合模式用于将对象组织成树形结构,享元模式可以用于共享组合中的相似对象,以减少内存占用。这在图形和图像处理应用中特别有用。

享元模式可以与许多其他设计模式一起使用,具体取决于系统的需求。它通常与创建型模式(如工厂模式、单例模式)和结构型模式(如装饰者模式、代理模式)结合使用,以实现更灵活、高效和可维护的系统。在实际应用中,将多种模式结合使用可以更好地满足复杂系统的需求。

9 小结

享元模式是一种有助于减少内存占用和提高系统性能的结构型设计模式。通过共享大量细粒度的对象,它可以有效地降低系统的资源消耗,特别适用于存在大量相似对象的场景。在设计和开发中,当需要创建大量相似对象时,可以考虑使用享元模式以提高系统的效率和性能。这种模式的核心思想是将对象的内部状态与外部状态分离,从而实现对象的共享和复用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/116276.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023华为杯数学建模竞赛E题

一、前言 颅内出血&#xff08;ICH&#xff09;是由多种原因引起的颅腔内出血性疾病&#xff0c;既包括自发性出血&#xff0c;又包括创伤导致的继发性出血&#xff0c;诊断与治疗涉及神经外科、神经内科、重症医学科、康复科等多个学科&#xff0c;是临床医师面临的重要挑战。…

免费获取独立ChatGPT账户!!

GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。如在科研编程、绘图领域&#xff1a;1、编程建议和示例代码: 无论你使用的编程语言是Python、R、MATLAB还是其他语言&#xff0c;都可以为你提供相关的代码示例。2、数据可视化…

2023 年 Android 毕业设计选题推荐,200 道 Android 毕业设计题目,避免踩坑

前言 选择一个Android毕业设计题目是一个重要的决策&#xff0c;它将影响你未来几个月的工作。以下是一些关于如何选择一个合适的Android毕业设计题目以及如何避免踩坑的建议&#xff1a; 兴趣和热情&#xff1a;首先&#xff0c;选择你真正感兴趣的领域。如果你对某个领域充…

Python:Django框架的Hello wrold示例

Django是Python的目前很常用的web框架&#xff0c;遵循MVC设计模式。 以下介绍如何安装Django框架&#xff0c;并生成最简单的项目&#xff0c;输出Hello world。(开发工具VScode) 一、安装Django 在VScode终端控制台执行以下指令安装Django python install django 如果要查…

相机有俯仰角时如何将像素坐标正确转换到其他坐标系

一般像素坐标系转相机坐标系都是默认相机是水平的&#xff0c;没有考虑相机有俯仰角的情况&#xff0c;大致的过程是&#xff1a;像素坐标系统-->图像坐标系-->相机坐标系 ->世界坐标系或雷达坐标系: 像素坐标系 像素坐标系&#xff08;u&#xff0c;v&#xff09;是…

AIX360-CEMExplainer: MNIST Example

CEMExplainer: MNIST Example 这一部分屁话有点多&#xff0c;导包没问题的话可以跳过加载MNIST数据集加载经过训练的MNIST模型加载经过训练的卷积自动编码器模型&#xff08;可选&#xff09;初始化CEM解释程序以解释模型预测解释输入实例获得相关否定&#xff08;Pertinent N…

停车场系统源码

源码下载地址&#xff08;小程序开源地址&#xff09;&#xff1a;停车场系统小程序&#xff0c;新能源电动车充电系统&#xff0c;智慧社区物业人脸门禁小程序: 【涵盖内容】&#xff1a;城市智慧停车系统&#xff0c;汽车新能源充电&#xff0c;两轮电动车充电&#xff0c;物…

基于Android+OpenCV+CNN+Keras的智能手语数字实时翻译——深度学习算法应用(含Python、ipynb工程源码)+数据集(三)

目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据预处理2. 数据增强3. 模型构建4. 模型训练及保存1&#xff09;模型训练2&#xff09;模型保存 5. 模型评估 相关其它博客工程源代码下载其它资料下载 前言 本项目依赖于Keras深度学习模型&#xff0c;旨在对…

雷达编程实战之静态杂波滤除与到达角估计

雷达中经过混频的中频信号常常混有直流分量等一系列硬件设计引入的固定频率杂波&#xff0c;我们称之位静态杂波&#xff0c;雷达信号处理需要把这些静态杂波滤除从而有效的提高信噪比&#xff0c;实现准确的目标检测功能。 目标的到达角估计作为常规车载雷达信号处理的末端&am…

机器视觉康耐视Visionpro-脚本编写标记标识:点,直线,矩形,圆

显示标记标识的重要作用就是,对NG或者OK对操作机器视觉的人去看到具体位置缺陷或者NG坐标。 一.点CogPointMarker CogPointMarker PointMarker1 = new CogPointMarker();//创建对象,点CogPointMarker //注意运行工具 PointMarker1.X = 100; PointMarker1

多线程带来的的风险-线程安全

多线程带来的的风险-线程安全 ~~ 多线程编程中,最难的地方,也是一个最重要的地方&#xff0c;还是一个最容易出错的地方,更是一个面试中特别爱考的地方.❤️❤️❤️ 线程安全的概念 万恶之源,罪魁祸首是多线程的抢占式执行,带来的随机性.~~&#x1f615;&#x1f615;&…

人与机器只能感知到可以分类的事物?

众所周知&#xff0c;人与机器都能够感知和分类事物。人类拥有感官系统&#xff0c;如视觉、听觉、嗅觉、触觉和味觉&#xff0c;可以通过感知事物的外部特征和属性来进行分类。机器可以通过传感器和算法来感知和分类事物&#xff0c;比如计算机视觉技术可以通过图像和视频数据…