设计模式第14讲——享元模式(Flyweight)

news2024/11/16 0:45:22

目录

一、什么是享元模式

二、角色组成

三、优缺点

四、应用场景

4.1 生活场景

4.2 java场景

五、代码实现

5.0 代码结构

 5.1 Bike——抽象享元类(FlyWeight)

 5.2 具体享元类(ConcreteFlyWeight)

5.3 BikeFactory——享元工厂(FlyWeightFactory)

5.4 testFlyWeight

六、总结


最后一篇结构型的设计模式——享元模式

一、什么是享元模式

享元模式是一种结构型的设计模式。它的主要目的是通过共享对象来减少系统种对象的数量,其本质就是缓存共享对象,降低内存消耗

享元模式将需要重复使用的对象分为两个部分:内部状态和外部状态。

内部状态是不会变化的,可以被多个对象共享,而外部状态会随着对象的使用而改变。比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。

二、 角色组成

  • 抽象享元角色(FlyWeight):享元对象抽象基类或者接口,同时定义出对象的外部状态和内部状态的接口或实现;
  • 具体享元角色(ConcreteFlyWeight):实现抽象享元类中的方法,是需要共享的对象类
  • 享元工厂(FlyWeightFactory):维护一个享元对象的池,内部使用一个 Map 存储已经创建的享元对象

三、优缺点

优点:

  • 共享大量相似对象,可以避免大量无谓的内存分配和释放操作,缓解垃圾回收器的压力,提高吞吐量。
  • 避免对象的重复创建,尤其是在创建大量具有相同属性的对象时,使用享元模式可以大大提高系统的性能和效率,避免出现大量的重复对象占用内存空间,降低系统开销。
  • 享元模式的内部状态和外部状态是分离的,内部状态存储在内存中,外部状态可以根据需要进行传递,这样可以有效地降低对象间的耦合度。
  • 共享的对象能够在多个线程中安全地共享并发访问,不会有线程安全问题。

缺点:

  • 实现比较复杂。在实际开发中,需要对内部状态和外部状态进行较为繁琐的区分。
  • 过度共享。如果对象的内部和外部状态没有分清,或者没有合理地设计,可能会导致系统过度共享,导致共享的对象过多。
  • 增加代码的复杂度。程序员必须时刻根据系统的实际情况以及内部状态和外部状态的不同选择使用对象池或享元工厂来管理内存和共享对象,进一步增加了系统的复杂度。

四、应用场景

4.1 生活场景

  • 共享单车:在共享单车系统中,每辆车可以看做是一个享元对象。共享的部分是单车的基本属性,比如颜色、价格、型号等。相同型号、价格、颜色的单车可以共享同一个对象,不需要每次创建新的对象。
  • 车票预订:在一个车站的车票预订系统中,有许多订票窗口,每个窗口可以处理多个订票请求。每个订票请求都可以看作一个享元对象,而订票窗口可以看作是“享元工厂”。订票窗口根据请求的参数来创建或共享对象,避免创建过多的重复对象,提高了系统的性能。
  • 汉字输入法:在汉字输入法中,由于汉字数量很多,为了避免重复创建,使用享元模式,即相同的汉字只需要在内存中创建一次,这样就大大减少了内存的消耗,也加快了输入法的执行速度。

4.2 java场景

  • String 类型:内部维护了一个字符串对象池(即字符串常量池),相同的字符串只会在池中创建一次,之后它们会被多个对象共享,这样可以减少字符串对象的数量,降低内存的使用,并提升系统的效率。
  • Integer 类型:对常用的整数值(-127~128)进行了缓存,避免了每次创建新的 Integer 对象,提高了系统的性能。
  • 数据库连接池:数据库连接是非常消耗资源的,通过享元模式将连接池内部的连接对象进行复用,减少连接对象的创建和销毁,提高系统的效率和性能。
  • 线程池:通过享元模式将线程池中创建的线程对象进行复用,减少了线程的创建和销毁,从而提高系统的性能。

五、代码实现

下面以共享单车为例,解释一下享元模式。每一辆车可以看做是一个享元对象,共享的部分为颜色和价格。

5.0 代码结构

 5.1 Bike——抽象享元类(FlyWeight)

/**
 * @author Created by njy on 2023/6/21
 * 1.抽象享元类(FlyWeight):单车
 * 单车有很多共同的信息比如:价格、颜色
 */
public abstract class Bike {

    protected String color;

    protected int price;

    public Bike(String color,int price){
        this.color=color;
        this.price=price;
    }

    //展示单车信息
    public abstract void show();

}

 5.2 具体享元类(ConcreteFlyWeight)

/**
 * @author Created by njy on 2023/6/21
 * 2.具体享元类(ConcreteFlyWeight):摩拜单车
 */
public class Mobike extends Bike{

    public Mobike(String color,int price){
        super(color,price);
    }

    @Override
    public void show() {
        System.out.println("生产成功:摩拜单车——"+color+","+"起步价"+price+"元");
    }
}
/**
 * @author Created by njy on 2023/6/21
 * 2.具体享元类(ConcreteFlyWeight):美团单车
 */
public class MTBike extends Bike{

    public MTBike(String color,int price){
        super(color,price);
    }

    @Override
    public void show() {
        System.out.println("生产成功:小黄车——"+color+","+"起步价"+price+"元");
    }
}

5.3 BikeFactory——享元工厂(FlyWeightFactory)

/**
 * @author Created by njy on 2023/6/21
 * 3.享元工厂类(FlyWeightFactory):单车工厂
 */
public class BikeFactory {
    //map缓存
    public static Map<String,Bike> bikeMap=new HashMap<>();
    public static Bike getBike(String color,String type){
        String key=color+"_"+type;
        if(bikeMap.containsKey(key)){
            //如果已经有该颜色和类型的单车,直接返回
            return bikeMap.get(key);
        }else {
            Bike bike=null;
            //没有,创建并放入缓存
            if("mobike".equals(type)){
                bike=new Mobike(color,1);
            }else if ("MT".equals(type)){
                bike=new MTBike(color,2);
            }else {
                System.out.println("抛异常!没有该类型的单车");
            }
            //放入缓存
            bikeMap.put(key,bike);
            return bike;
        }
    }
}

5.4 testFlyWeight

/**
 * @author Created by njy on 2023/6/21
 * 享元模式测试类
 */
@SpringBootTest
public class TestFlyweight {
    @Test
    void testFlyweight(){
        //摩拜单车
        Bike bike1 = BikeFactory.getBike("黑色", "mobike");
        //美团单车
        Bike bike2 = BikeFactory.getBike("黑色", "MT");
        bike1.show();
        bike2.show();
        System.out.println("总共有"+BikeFactory.bikeMap.size()+"种类型单车");

        System.out.println("--------创建相同类型和颜色的单车----------");
        Bike bike3 = BikeFactory.getBike("黑色", "mobike");
        bike3.show();
        //再次看有多少类型
        System.out.println("总共有"+BikeFactory.bikeMap.size()+"种类型单车");
    }
}

六、总结

享元模式其实就是工厂模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法生成对象的,不同的地方在于享元模式增加了缓存的功能。

主要的使用场景:

  1. 当程序中需要创建大量的相似对象时,可以使用享元模式来共享相同的内部状态,减少对象的重复创建,节省内存开销。
  2. 当系统运行效率较低,存在大量的内存开销时,可以采用享元模式来优化内存使用,加快程序的执行速度。
  3. 当系统中的对象访问比较频繁时,可以使用享元模式来优化系统性能,减少重复对象的创建和销毁,提高程序的运行效率。
  4. 当系统中需要共享一些公共对象或静态数据时,可以使用享元模式来降低系统的开销,提升系统的性能。
  5. 当系统需要考虑加强安全控制时,可以使用享元模式来保证对对象访问的控制,实现权限控制、缓存控制等功能。

从上述不难看出,享元模式适用于需要创建大量相似的对象以及共享公共信息的场景,同时也适用于需要考虑性能优化共享控制的系统。但是使用之前一定要考虑清楚,还是那句话,不要为了使用设计模式而去使用设计模式。

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

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

相关文章

layui框架学习(28:穿梭框模块)

Layui模块中的穿梭框模块transfer主要支撑穿梭框组件的显示、交互等操作。所谓穿梭框是指左右各有一个复选框列表&#xff0c;可以将左侧选中的项目移动到右边&#xff0c;后者将右侧的选中项移回左边的控件&#xff0c;其样式类似下图所示&#xff08;参考文献5-6&#xff09;…

TI AM62x工业开发板规格书(单/双/四核ARM Cortex-A53 + 单核ARM Cortex-M4F,主频1.4GHz)

1 评估板简介 创龙科技TL62x-EVM是一款基于TI Sitara系列AM62x单/双/四核ARM Cortex-A53 单核ARM Cortex-M4F多核处理器设计的高性能低功耗工业评估板&#xff0c;由核心板和评估底板组成。处理器ARM Cortex-A53(64-bit)主处理单元主频高达1.4GHz&#xff0c;ARM Cortex-M4F实…

如何使用 Flink SQL 探索 GitHub 数据集|Flink-Learning 实战营

为进一步帮助开发者学习使用 Flink&#xff0c;Apache Flink 中文社区近期发起 Flink-Learning 实战营项目。本次实战营通过真实有趣的实战场景帮助开发者实操体验 Flink&#xff0c;课程包括实时数据接入、实时数据分析、实时数据应用的场景实。并结合小松鼠助教模式&#xff…

USR-C216 WIIF连接手机

复位后连接USR-C216无线 浏览器输入10.10.100.254 账号密码为admin 客户端模式服务器地址无效&#xff0c;默认就行 打开手机网络调试助手选择客户端模式&#xff0c;输入10.10.100.254&#xff0c;端口8899 可以透传了 关于AT指令&#xff0c;先发“”&#xff0c;然后3s内发…

【数据管理架构】什么是 OLTP?

OLTP&#xff08;在线事务处理&#xff09;支持在 ATM 和在线银行、收银机和电子商务以及我们每天与之交互的许多其他服务背后进行快速、准确的数据处理。 什么是 OLTP&#xff1f; OLTP 或在线事务处理允许大量人员&#xff08;通常通过 Internet&#xff09;实时执行大量数据…

基于Vue+Node.js的宠物领养网站的设计与开发-计算机毕设 附源码83352

基于VueNode.js的宠物领养网站的 摘 要 随着互联网大趋势的到来&#xff0c;社会的方方面面&#xff0c;各行各业都在考虑利用互联网作为媒介将自己的信息更及时有效地推广出去&#xff0c;而其中最好的方式就是建立网络管理系统&#xff0c;并对其进行信息管理。由于现在网络…

【国产FPGA应用】紫光Pango Design联合 Modelsim 仿真方法

Modelsim 是 FPGA 开发中重要的 EDA 设计仿真工具&#xff0c;主要用于验证数字电路设计是否正确。我们经常用Xilinx的ISE或者Vivado与Modelsim进行联合仿真&#xff0c;其实国产FPGA开发工具也可以与Modelsim进行联合仿真&#xff0c;对于设计比较复杂的应用还是非常方便的&am…

创邻科技与浪潮信息KOS完成澎湃技术认证

近日&#xff0c;浙江创邻科技有限公司&#xff08;简称&#xff1a;创邻科技&#xff09;自主研发的Galaxybase图数据库系统与浪潮信息服务器操作系统KOS V5完成澎湃技术认证。创邻科技作为国内首个成熟的商业图数据库供应商&#xff0c;在同类厂商中率先完成认证。测试结果显…

vue3通过render函数实现一个菜单下拉框

背景说明 鼠标移动到产品服务上时&#xff0c;出现标红的下拉框。 使用纯css的方案实现最简单&#xff0c;但是没什么技术含量&#xff0c;弃之&#xff1b;使用第三方组件库&#xff0c;样式定制麻烦弃之。因此&#xff0c;我们使用vue3直接在页面创建一个dom作为下拉框吧。…

【经验分享】Docker容器部署方法说明

前 言 本案例适用开发环境&#xff1a; Windows开发环境&#xff1a;Windows 7 64bit、Windows 10 64bit Linux开发环境&#xff1a;Ubuntu 18.04.4 64bit 虚拟机&#xff1a;VMware15.1.0 Docker是一个开源的应用容器引擎&#xff0c;让开发者可打包他们的应用以及依赖包…

rust持续学习 声明宏

学习记录&#xff0c;都是学自圣经&#xff0c;macrobook啥的 https://doc.rust-lang.org/reference/macros-by-example.html macro_rules! bar {(3) > {println!("3");};(4) > {println!("4");}; }这个是入门例子&#xff0c;有点像match 调用就是…

【Java|多线程与高并发】线程池详解

文章目录 1. 线程池简介2. 创建线程池3. 工厂模式简介4. 线程池的使用5. 实现线程池6. ThreadPoolExecutor的构造方法讲解7. 线程池的线程数量,如何确定? 1. 线程池简介 Java线程池是一种用于管理和重用线程的机制&#xff0c;它可以在需要执行任务时&#xff0c;从线程池中获…

二叉树遍历方法——前、中、后序遍历(java)

二叉树结构&#xff1a; static class TreeNode{public char val;public TreeNode left;public TreeNode right;public TreeNode(char val) {this.val val;}Overridepublic String toString() {return this.val"";}} 一、前序遍历 前序遍历是一种访问二叉树的每一…

【shell脚本】沐风晓月跟你聊聊shell脚本中的case实战

前言 前面我们已经介绍了while及for循环&#xff0c;结合if语句可以构建一些简单的控制面板及菜单脚本&#xff0c;今天我们来探讨下case语句。 case选择语句&#xff0c;主要用于对多个选择条件进行匹配输出&#xff0c;与if elif语句结构类似&#xff0c;通常用于脚本传递输…

阵列模式合成第 I 部分:清零、窗口化和细化(附源码)

一、前言 本示例说明如何使用相控阵系统工具箱解决一些阵列合成问题。在相控阵设计应用中&#xff0c;通常需要找到一种方法来逐渐减小晶片响应&#xff0c;以使最终的阵列阵列模式满足某些性能标准。典型的性能标准包括主瓣位置、零位置和旁瓣电平。 二、使用旁瓣消除器消除干…

两个进程定时通过共享内存进行通信

进程1-client #include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/shm.h> #include <unistd.h> #include <string.h>#define SHM_SIZE 10 * 1024 * 1024 // 共享内存大小为10M #define WRITE_INTERVAL 1 …

PHP 基础知识

目录 PHP基础 2 PHP代码标记 2 PHP注释 2 PHP语句分隔符 2 PHP变量 3 常量 3 数据类型 4 流程控制 6 文件 7 函数 9 闭包 11 常用系统函数 12 错误处理 13 错误显示设置 15 字符串类型 17 字符串相关函数 19 数组 21 遍历数组 22 数组的相关函数 25 PHP基础 PHP是一种运行在服务…

通过netty源码带你一步步剖析NioEventLoop 的任务队列原理

NioEventLoop 的异步任务队列成员: NioEventLoop 中对newTaskQueue 接口的实现,返回的是JCTools工具包Mpsc队列(多生产者单一消费者无锁队列,(无界和有界都有实现) private static Queue<Runnable> newTaskQueue0(int maxPendingTasks) {// newMpscQueue 无界对列,newM…

10万元存款是年轻人的一个“坎”?存款超过10万就会超过53.7%的人?不要焦虑,以过来人的身份帮你分析分析!

&#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Micro麦可乐的博客 &#x1f425;《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程&#xff0c;入门到实战 &#x1f33a;《RabbitMQ》…

ChatGPT最新版实现多样化聚合文章的批量生成文章

随着人工智能技术的不断发展&#xff0c;ChatGPT最新版在多样化聚合文章的批量生成方面取得了重要突破。本文将从随机选取的8个方面&#xff0c;对ChatGPT最新版的构建思想进行详细阐述。这些方面包括&#xff1a;自然语言处理、大规模数据集、迁移学习、多模态输入、生成模型优…