设计模式(22)享元模式

news2025/2/26 9:32:33

一、介绍:

1、定义:享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

2、组成结构:

(1)Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。

public abstract class  Flyweight{
	public abstract void operation(String extrinsicState);
}

(2)ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。

public class ConcreteFlyweight extends Flyweight{
	//内部状态intrinsicState作为成员变量,同一个享元对象的内部状态是一致的
	private String intrinsicState;
	public ConcreteFlyweight(String intrinsicState){
		this.intrinsicState = intrinsicState;
	}

	//外部状态extrinsicState在使用时由外部设置,不保存在享元对象中,即使是同一个对象
	//在每一次调用时可以传入不同的外部状态
	public void operation(String extrinsicState){
		//实现业务方法
	}
}

(3)UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。

public class UnsharedConcreteFlyweight extends Flyweight{
	public void operation(String extrinsicState){
		//实现业务方法
	}
}

(4)FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。

public class FlyweightFactory{
	//定义一个HashMap用于存储享元对象,实现享元池
	private HashMap flyweights = new HashMap();

	public Flyweight getFlyweight(String key){
		//如果对象存在,则直接从享元池获取
		if(flyweight.containsKey(key)){
			return (Flyweight)flyweight.get(key);
		}
		//如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
		else{
			Flyweight fw = new ConcreteFlyweight();
			flyweights.put(key,fw);
			return fw;
		}
	}
}

3、享元模式将对象的状态分为内部状态和外部状态:

(1)内部状态:是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,如用户的ID、Name,它们可以作为一个对象的动态附加信息,不必直接储存在具体某个对象中,属于可以共享的部分。即在生成对象后,类的属性每个对象都不一样,那他就是内部状态,用户的ID是每个用户都不一样的。

(2)外部状态:是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态,它是一批对象的统一标识,是唯一的一个索引值。即在生成对象后,类的属性大部分对象或部分的值相同,那么这个属性就是外部状态,例如用户属于VIP用户,很多用户都属于VIP用户。

4、优缺点:

优点:

(1)减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率

(2)减少内存之外的其他资源占用 (减少new 关键字的创建次数)

缺点:

(1)关注内/外部状态、关注线程安全问题

(2)使系统、程序的逻辑复杂化

5、相关设计模式:

(1)代理模式:代理模式是需要代理一个类,需要生成的代理类花费的资源和时间比较多就可以使用享元模式提高;

(2)单例模式:容器单例模式就是享元模式和单例模式的结合,使用复用的思想提高程序的使用频率。

6、使用场景:

二、demo:

1、黑白棋:黑白棋游戏需要大量的棋子对象,如果为每一个棋子都单独创建一个对象,系统的性能和内存开销都非常大。使用享元模式,可以将一些相同的棋子对象共享起来,只需要保存其内部和外部状态区分即可。

/**
 * 抽象的棋子接口
 */
public interface Piece {
    void put(int x, int y);
}


/**
 * 黑棋类
 */
public class BlackPiece implements Piece {
    @Override
    public void put(int x, int y) {
        System.out.println("在坐标 (" + x + ", " + y + ") 放置了一个黑棋子");
    }
}


/**
 * 白棋类
 */
public class WhitePiece implements Piece {
    @Override
    public void put(int x, int y) {
        System.out.println("在坐标 (" + x + ", " + y + ") 放置了一个白棋子");
    }
}

//棋子享元工厂类
public class PieceFactory {
    private final Map<String, Piece> map = new HashMap<>();
    public Piece getPiece(String color) {
        Piece piece = map.get(color);
        if (piece == null) {
            switch (color) {
                case "black":
                    piece = new BlackPiece();
                    break;
                case "white":
                    piece = new WhitePiece();
                    break;
                default:
                    throw new IllegalArgumentException("Unsupported color: " + color);
            }
            map.put(color, piece);
        }
        return piece;
    }
}
//客户端
 public static void main(String[] args) {
        PieceFactory factory = new PieceFactory();
        Piece black1 = factory.getPiece("black");
        black1.put(1, 1);
        Piece black2 = factory.getPiece("black");
        black2.put(2, 2);
        Piece white1 = factory.getPiece("white");
        white1.put(3, 3);
        Piece white2 = factory.getPiece("white");
        white2.put(4, 4);
    }

输出:
在坐标 (1, 1) 放置了一个黑棋子
在坐标 (2, 2) 放置了一个黑棋子
在坐标 (3, 3) 放置了一个白棋子
在坐标 (4, 4) 放置了一个白棋子

2、扑克牌游戏:54张扑克牌(1到13四种花色)+大王+小王:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3中解析地址(address-parse插件的使用)

1.安装 npm install address-parse --save 2.使用 // 引入address-parse import AddressParse, { AREA, Utils } from "address-parse";const adressValue ref([])const getResolutionContent () > {const [result] AddressParse.parse(EnterpriseSalesDetai…

Android APT的使用

Apt 介绍 APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的 Annotation&#xff0c;根据注解自动生成代码。 Annotation 处理器在处理 Annotation 时可以根据源文件中的 Annotation 生成额外的源文件和其它的文件(文件具体内容由 Annot…

Java实现Hive UDF详细步骤 (Hive 3.x版本,IDEA开发)

这里写目录标题 前言1. 新建项目2.配置maven依赖3.编写代码4.打jar包5.上传服务器6.代码中引用 前言 老版本编写UDF时&#xff0c;需要继承 org.apache.hadoop.hive.ql.exec.UDF类&#xff0c;然后直接实现evaluate()方法即可。 由于公司hive版本比较高&#xff08;3.x&#x…

Linux入门指令和权限讲解

目录 一&#xff0c;Linux指令讲解 1. ls 指令&#xff08;查看文件&#xff09; 2. pwd命令&#xff08;展现当前工作目录&#xff09; 3. cd 指令&#xff08;改变当前所处工作目录&#xff09; 4. touch指令&#xff08;创建文件&#xff09; 5.mkdir指令&#xff08;创…

高效处理异常值的算法:One-class SVM模型的自动化方案

一、引言 数据清洗和异常值处理在数据分析和机器学习任务中扮演着关键的角色。清洗数据可以提高数据质量&#xff0c;消除噪声和错误&#xff0c;从而确保后续分析和建模的准确性和可靠性。而异常值则可能对数据分析结果产生严重影响&#xff0c;导致误导性的结论和决策。因此&…

【Java题】写一个泛型类,实现一个方法,求指定类型的数组中的最大值

一&#xff1a;题目 写一个泛型类&#xff0c;实现一个方法&#xff0c;求指定类型的数组中的最大值 二&#xff1a;分析 要注意实现Comparable接口&#xff0c;才能在不同类型比较时使用compareTo 三&#xff1a;代码 class Alg<T extends Comparable<T>>{public…

系统韧性研究(3)| 工程系统韧性要求

从最基本的层面上说&#xff0c;系统韧性指的是系统在逆境中继续执行其任务的程度。虽然对操作连续性至关重要&#xff0c;但系统的服务&#xff08;能力&#xff09;只是系统继续执行其任务所必须保护的一些资产。该系统必须检测不利因素&#xff0c;对其作出反应&#xff0c;…

java入门,程序=数据结构+算法

一、前言 在学习java的时候&#xff0c;我印象最深的一句话是&#xff1a;程序数据结构算法&#xff0c;对于写java程序来说&#xff0c;这就是java的入门。 二、java基本数据结构与算法 1、数据类型 java中的数据类型8种基本数据类型&#xff1a; 整型 byte 、short 、int…

【C++模板】template的使用

普通函数的模板 #include <iostream> #include <array> using namespace std;typedef int* pInt; //重命名整形指针 template<typename FormatOutput> //模板 void fo(_In_ const FormatOutput& obj, bool EndFlag false) { //格式化输出&#xf…

海上风电应急救援vr模拟安全培训提高企业风险防范能力

相比传统的发电厂&#xff0c;海上风电作业积累的经验少&#xff0c;风险高&#xff0c;因此为了规范施工人员的行为和操作&#xff0c;保障生产安全进行&#xff0c;开展海上风电VR安全培训具有重要意义。 有助于提高员工的安全意识 通过模拟真实的海上风电作业环境&#xff0…

hadoop hdfs的API调用,在mall商城代码中添加api的调用

在网上下载了现成的商城代码的源码 本次旨在熟悉hdfs的api调用&#xff0c;不关注前后端代码的编写&#xff0c;所以直接下载现成的代码&#xff0c;代码下载地址。我下载的是前后端在一起的代码&#xff0c;这样测试起来方便 GitHub - newbee-ltd/newbee-mall: &#x1f525; …

VulnHub DC-2

一、信息收集 1.nmap扫描 扫描开放端口&#xff0c;发现只开启了80、7744端口 ┌──(root&#x1f480;kali)-[~/桌面] └─# nmap --scriptvuln -p80,7744 192.168.103.190--scriptvuln 可以检查目标主机或网段是否存在常见的漏洞 2.添加hosts 浏览器访问http://192.168.…

ipad协议

逆向工程是一种强大的工具&#xff0c;可以帮助我们理解协议、识别漏洞&#xff0c;并开发兼容的应用程序或扩展。它使我们能够更深入地了解复杂系统并创建创新的解决方案。iPad协议是苹果生态系统不可或缺的一部分&#xff0c;它允许iPad和其他设备之间实现无缝通信。为了理解…

[PyTorch][chapter 58][强化学习-2-有模型学习]

前言&#xff1a; 在已知模型的环境里面学习,称为有模型学习&#xff08;model-based learning&#xff09;. 此刻,下列参数是已知的&#xff1a; : 在状态x 下面,执行动作a ,转移到状态 的概率 : 在状态x 下面,执行动作a ,转移到 的奖赏 有模型强化学习的应用案例 …

App渗透测试有哪些测试方法?可进行移动app测试的公司推荐

App渗透测试是当前互联网时代中不可或缺的重要环节。随着智能手机的普及和App应用的广泛使用&#xff0c;App渗透测试帮助企业保障其移动应用的安全性和可靠性&#xff0c;确保用户的隐私和敏感信息不会被黑客窃取。那么&#xff0c;什么是App渗透测试呢?它有哪些测试方法?接…

java实现pdf文件添加水印,下载到浏览器

java实现pdf文件添加水印&#xff0c;下载到浏览器 添加itextpdf依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.8</version> </dependency>文件下载到浏览器和指定路径 …

第四章 数据结构与算法——树与二叉树

一、树的定义 ①&#xff1a;树是一种 非线性 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个有限结点组成一个具有层次关系的集合。 ②&#xff1a; 把它叫做树是因 为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的 。 …

BetterDisplay Pro v1.4.15(显示器管理管理软件)

BetterDisplay Pro是一款屏幕显示优化工具&#xff0c;可用于Windows和Mac操作系统。它可以帮助用户调整屏幕的亮度、对比度、色彩等参数&#xff0c;以获得更好的视觉体验。此外&#xff0c;BetterDisplay Pro还提供了一些额外的功能&#xff0c;如屏幕分割、窗口管理、快捷键…

变量的作用域

在javascript中&#xff0c;var定义的变量实际是有作用域的。 1 假设在函数体内声明&#xff0c;但在函数体外不可以使用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </…

HBase理论与实践-基操与实践

基操 启动&#xff1a; ./bin/start-hbase.sh 连接 ./bin/hbase shell help命令 输入 help 然后 <RETURN> 可以看到一列shell命令。这里的帮助很详细&#xff0c;要注意的是表名&#xff0c;行和列需要加引号。 建表&#xff0c;查看表&#xff0c;插入数据&#…