当前位置: 首页 > news >正文

秒懂设计模式--学习笔记(11)【结构型-享元模式】

目录

      • 10、享元模式
        • 10.1 享元模式
        • 10.2 举例
          • 10.2.1 马赛克
          • 10.2.2 游戏地图(以草原地图作为范例)
        • 10.3 总结

10、享元模式

10.1 享元模式
  • “享元”则是共享元件的意思
  • 享元模式的英文flyweight是轻量级的意思,这就意味着享元模式能使程序变得更加轻量化
  • 当系统存在大量的对象,并且这些对象又具有相同的内部状态时,我们就可以用享元模式共享相同的元件对象,以避免对象泛滥造成资源浪费。
  • 测试类结构
    享元模式测试类结构
10.2 举例
10.2.1 马赛克
  • 虽然马赛克小块数量比较多,但经过观察我们会发现,
  • 分析组成,进行归类后,找到元件只有4种:黑色块、灰色块、灰白色块以及白色块。
  • 我们可以说,这就是4个“元”色块
10.2.2 游戏地图(以草原地图作为范例)
  • 对组成部分进行归类分析,找到原件
    • 游戏地图都是由一个个小的单元图块组成的
    • 其中除房屋比较大之外,其他图块的尺寸都一样,它们分别为河流、草地、道路,这些图块便是4个元图块
  • 分析建模
    • 定义一个图块类来描述图块,具体属性应该包括“图片”和“位置”信息,并且具备按照这些信息去绘制图块的能力:Segment
    package flyweight;/*** @Description 图块类*/
    public class Segment {/*** 材质图*/private String image;/*** 位置坐标*/private int x,y;/*** 显式带参构造方法:初始化各参数* @param image 材质图* @param x 横坐标* @param y 纵坐标*/public Segment(String image, int x, int y) {this.image = image;System.out.println("从磁盘加载[" + image + "]图片……");this.x = x;this.y = y;}/*** 图块绘制方法:按坐标位置绘制在地图上*/public void draw() {System.out.println("在位置[" + x + ":" + y + "]上绘制图片:[" + image + "]");}
    }
    
    • 在地图第一行随便绘制一些图块,Client.test1()
      • 在这一步会发现,图片加载很慢,一张图片加载要半秒,10张图块就要耗费5秒,影响用户体验
    package flyweight;/**
    *@Description 测试类
    */
    public class Client {private static void test1() {//在地图第一行随便绘制一些图块new Segment("河流", 10, 10).draw();new Segment("河流", 10, 20).draw();new Segment("道路", 10, 30).draw();new Segment("草地", 10, 40).draw();new Segment("草地", 10, 50).draw();new Segment("草地", 10, 60).draw();new Segment("草地", 10, 70).draw();new Segment("草地", 10, 80).draw();new Segment("道路", 10, 90).draw();new Segment("道路", 10, 100).draw();}
    }
    
    • 图片与坐标状态初始化后就固定下来了,简单讲就是被绘制出来后就不必变动了,即使要变也是将拼好的地图作为一个大对象整体挪动
  • 图件共享(优化)
    • 继续分析每个图块的坐标是不同的,但有很大一部分图块的材质图(图片)是相同
    • 于是我们可以得出结论,材质图是可以作为享元的,而坐标则不能
    • 既然要共享相同的图片,那么我们就得将图块类按图片拆分成更细的材质类,如河流类、草地类、道路类等
    • 而坐标不能作为图块类的享元属性,所以我们就得设法把这个属性抽离出去由外部负责
    • 代码实战
      • 首先需要定义一个接口,规范这些材质类的绘图标准(接口:规范标准Drawable)
        • 当然,除了接口方式,我们还可以用抽象类抽离出更多的属性和方法,使子类变得更加简单
    package flyweight;
    /**
    * @Description 绘图接口: 规范这些材质类的绘图标准
    *                  当然,除了接口方式,我们还可以用抽象类抽离出更多的属性和方法,使子类变得更加简单
    */
    public interface DrawAble {
    /**
    * 绘图方法,接收地图坐标
    * @param x 横坐标
    * @param y 纵坐标
    */
    void draw(int x, int y);
    }
    
    • 定义一系列材质类并实现此绘图接口
      • 河流类River
      package flyweight.texture;
      import flyweight.DrawAble;
      /*** @Description 河流类*/
      public class River implements DrawAble {/*** 享元属性: 河流图片材质作为内部属性*/private String image;/*** 类构造器中加载河流图片*      这就是类内部即将共享的“元”数据了,我们通常称之为“内蕴状态”*/public River() {this.image = "河流";System.out.print("从磁盘加载[" + image + "]图片,耗时……");}/*** 重写绘图方法* 而作为“外蕴状态”的坐标是无法作为享元的,由外部传入* @param x 横坐标* @param y 纵坐标*/@Overridepublic void draw(int x, int y) {System.out.println("在位置[" + x + ":" + y + "]上绘制图片:[" + image + "]");}
      }
      
      • 草地类Grass
       package flyweight.texture;import flyweight.DrawAble;/*** @Description 草地类*/public class Grass implements DrawAble {/*** 享元属性:草地图片材质*/private String image;public Grass() {this.image = "草地";System.out.print("从磁盘加载[" + image + "]图片,耗时……");}@Overridepublic void draw(int x, int y) {System.out.println("在位置[" + x + ":" + y + "]上绘制图片:[" + image + "]");}}
      
      • 道路类Road
      	package flyweight.texture;import flyweight.DrawAble;/*** @Description 道路类*/public class Road implements DrawAble {/*** 道路图片材质*/private String image;public Road() {this.image = "道路";System.out.println("从磁盘加载[" + image + "]图片,耗时…");}@Overridepublic void draw(int x, int y) {System.out.println("在位置[" + x + ":" + y + "]上绘制图片:[" + image + "]");}}
      
      • 房屋类House
       package flyweight.texture;import flyweight.DrawAble;/*** @Description 房屋类*/public class House implements DrawAble {private String image;//房屋图片材质public House() {this.image = "房屋";System.out.print("从磁盘加载[" + image + "]图片,耗时……");}@Overridepublic void draw(int x, int y) {System.out.print("将图层切换到顶层……");//房屋盖在地板上,所以切换到顶层图层System.out.println("在位置[" + x + ":" + y + "]上绘制图片:[" + image + "]");}}
      
    • “元之共享”的关键:
      • 定义一个图件工厂类
       package flyweight.factory;import flyweight.DrawAble;import flyweight.texture.Grass;import flyweight.texture.House;import flyweight.texture.River;import flyweight.texture.Road;import java.util.HashMap;import java.util.Map;/*** @Description 图件工厂类*/public class SegmentFactory {/*** 图库: 维护着所有的图件元对象*/private Map<String, DrawAble> images;public SegmentFactory() {images = new HashMap<String, DrawAble>();}public DrawAble getDrawable(String image) {//缓存池里如果没有图件,则实例化并放入缓存池if(!images.containsKey(image)){switch (image) {case "河流":images.put(image, new River());break;case "草地":images.put(image, new Grass());break;case "道路":images.put(image, new Road());break;case "房屋":images.put(image, new House());}}//至此,缓存池里必然有图件,直接取得并返回return images.get(image);}}
      
      • 并将各种图件对象提前放入内存中共享,如此便可以避免每次从磁盘重新加载
    • 测试:Client.test2()
          private static void test2() {//先实例化图件工厂SegmentFactory factory = new SegmentFactory();/** 随便绘制一列为例:*      抛弃了利用“new”关键字随意制造对象的方法,*      改用这个图件工厂类来构建并共享图件元,外部需要什么图件直接向图件工厂索要即可*/factory.getDrawable("河流").draw(10, 10);factory.getDrawable("河流").draw(10, 20);factory.getDrawable("道路").draw(10, 30);factory.getDrawable("草地").draw(10, 40);factory.getDrawable("草地").draw(10, 50);factory.getDrawable("草地").draw(10, 60);factory.getDrawable("草地").draw(10, 70);factory.getDrawable("草地").draw(10, 80);factory.getDrawable("道路").draw(10, 90);factory.getDrawable("道路").draw(10, 100);//绘制完地板后接着在顶层绘制房屋factory.getDrawable("房子").draw(10, 10);factory.getDrawable("房子").draw(10, 50);}
      
    • 小结
      • 相同部分可以作为享元,如在构造器中加载的,作为内部类即将共享的元数据,通常称为“内蕴状态”
      • 不同部分不能作为享元,如在实现房中作为参数传入的属性,称为“外蕴状态”
10.3 总结
  • 享元模式让图件对象将可共享的内蕴状态“图片”维护起来,将外蕴状态“坐标”抽离出去并定义于接口参数中
  • 基于此,享元工厂便可以顺利将图件对象共享,以供外部随时使用。
  • 享元模式的各角色定义如下
    • Flyweight(享元接口):所有元件的高层规范,声明与外蕴状态互动的接口标准。如:DrawAble。
    • ConcreteFlyweight(享元实现):
      • 享元接口的元件实现类,自身维护着内蕴状态,且能接受并响应外蕴状态,
      • 可以有多个实现。如:河流类River、草地类Grass、道路类Road等。
      • 一个享元对象可以被称作一个“元”
    • FlyweightFactory(享元工厂):用来维护享元对象的工厂,负责对享元对象实例进行创建与管理,并对外提供获取享元对象的服务。SegmentFactory
    • Client(客户端):享元的使用者,负责维护外蕴状态。Client
  • “享元”的理念其实就是萃取事物的本质
  • 将对象的内蕴状态与外蕴状态剥离开来,其中内蕴状态成为真正的“元”数据,而外蕴状态则被抽离出去由外部负责维护

相关文章:

秒懂设计模式--学习笔记(11)【结构型-享元模式】

目录 10、享元模式10.1 享元模式10.2 举例10.2.1 马赛克10.2.2 游戏地图&#xff08;以草原地图作为范例&#xff09; 10.3 总结 10、享元模式 10.1 享元模式 “享元”则是共享元件的意思享元模式的英文flyweight是轻量级的意思&#xff0c;这就意味着享元模式能使程序变得更…...

Python爬虫——1爬虫基础(一步一步慢慢来)

一、爬虫是什么&#xff1f; &#xff08;spider&#xff09; Python 爬虫是利用编程语言 Python 来获取互联网上的数据的技术。它可以自动化地访问网页、提取信息并进行数据处理。以下是Python爬虫的基础知识和步骤&#xff1a; 主要特点和功能&#xff1a; 自动化浏览&#…...

【js自学打卡9】抛出异常 / 幂计算 / 发布订阅 / map小知识点

1. 抛出异常的写法 抛出一个简单的字符串错误 throw Error2; // 抛出一个字符串抛出一个Error对象 throw new Error(出错了&#xff01;);抛出一个自定义错误对象 function UserError(message) {this.message message;this.name "UserError"; } throw new User…...

ArcGIS Pro SDK (九)几何 7 多点

ArcGIS Pro SDK &#xff08;九&#xff09;几何 7 多点 文章目录 ArcGIS Pro SDK &#xff08;九&#xff09;几何 7 多点1 构造多点 - 从映射点的枚举2 构造多点 - 使用 MultipointBuilderEx3 修改多点的点4 从多点检索点、2D 坐标、3D 坐标 环境&#xff1a;Visual Studio 2…...

服务器注意事项

1. 远程服务器不允许关机&#xff0c;只能重启&#xff1b; 2. 重启服务器应关闭服务&#xff1b; 3. 不要在服务器访问高峰运行高负载命令&#xff1b; 4. 远程配置防火墙是不要把自己踢出服务器&#xff1b; 5. 制定合理的密码规范并定期更新&#xff1b; 6. 合理分配权…...

学生信息管理系统设计

学生信息管理系统的设计是一个综合性的项目&#xff0c;涉及到数据的存储、检索、更新和删除等基本操作&#xff0c;同时也需要考虑系统的易用性、安全性和扩展性。以下是一些关键步骤和要素&#xff0c;用于指导设计这样一个系统&#xff1a; 1. 需求分析 目标用户&#xff…...

Python求均值,方差,标准差

参考链接&#xff1a;变异系数&#xff08;Coefficient of Variation,COV&#xff09;和协方差&#xff08;Covariance, Cov&#xff09;-CSDN博客 参考链接&#xff1a;pandas中std和numpy的np.std区别_numpy pandas std-CSDN博客 在计算蛋白质谱数据中的每个蛋白对应的变异…...

DDei在线设计器-HTML渲染

Html渲染 HtmlViewer插件通过将一个外部DIV附着在图形控件上&#xff0c;从而改变原有图形的显示方式。允许使用者自己定义HTML通过HTML元素。本示例演示了通过Html来扩展渲染图形&#xff0c;从而获得更加丰富的图形展现。 通常情况下&#xff0c;我们创建的图形控件&#xff…...

【React Hooks原理 - useSyncExternalStore】

概述 在React项目中说到状态管理&#xff0c;我们第一时间想到的就是使用useState、useReducer这种Hooks来进行状态管理。但是这种是针对React内部的状态&#xff0c;如果有时候我们需要订阅外部的状态并影响React组件的更新的话&#xff0c;那通过这种内部状态管理API显然不能…...

C++STL初阶(7):list的运用与初步了解

在了解了vector之后&#xff0c;我们只需要简单学习List与vector不一样的接口即可 1.list的基本接口 1.1 iterator list中&#xff0c;与vector最大的区别就是迭代器由随机迭代器变成双向迭代器 string和vector中的迭代器都是随机迭代器&#xff0c;支持-等&#xff0c;而LIS…...

el-menu弹出菜单样式不生效

1. 使用 ruoyi 项目时出现的问题。 <template><el-menu:default-active"activeMenu":collapse"false":unique-opened"true"class"container":collapse-transition"true"mode"horizontal"><sideba…...

Springboot 3.x - Reactive programming (2)

三、WebFlux Blocking Web vs. Reactive Web Blocking Web (Servlet) and Reactive Web (WebFlux) have significant differences in several aspects. 1. Front Controller Servlet-Blocking Web: Uses DispatcherServlet as the front controller to handle all HTTP req…...

WPF+Mvvm 项目入门完整教程(一)

WPF+Mvvm 入门完整教程一 创建项目MvvmLight框架安装完善整个项目的目录结构创建自定义的字体资源下载更新和使用字体资源创建项目 打开VS2022,点击创建新项目,选择**WPF应用(.NET Framework)** 创建一个名称为 CommonProject_DeskTop 的项目,如下图所示:MvvmLight框架安装…...

[解决方法]git上传的项目markdown文件的图片无法显示

应该有不少初学者会遇到这种情况 以下是本人摸索出的解决方法 我使用的是typora&#xff0c;首先设置typora的图片设置 文件>偏好设置>图像 如下&#xff1a; 选择这个就会在此文件的同级目录下创建一个assets文件夹来存放此markdown文件的所有图片 然后勾选优先使用相…...

【React】使用 antd 加载组件实现 iframe 的加载效果

文章目录 代码实现&#xff1a; import { Spin } from antd; import { useState } from react;export default function () {const [loading, setLoading] useState(true);return (<div style{{ position: relative, height: 100% }}><Spinstyle{{ position: absolu…...

Python爬虫(1) --基础知识

爬虫 爬虫是什么&#xff1f; spider 是一种模仿浏览器上网过程的一种程序&#xff0c;可以获取一些网页的数据 基础知识 URL 统一资源定位符 uniform resource locator http: 超文本传输协议 HyperText Transfer Protocol 默认端口 80 https: 安全的超文本传输协议 security…...

云原生系列 - Jenkins

Jenkins Jenkins&#xff0c;原名 Hudson&#xff0c;2011 年改为现在的名字。它是一个开源的实现持续集成的软件工具。 官方网站&#xff08;英文&#xff09;&#xff1a;https://www.jenkins.io/ 官方网站&#xff08;中文&#xff09;&#xff1a;https://www.jenkins.io…...

django踩坑(四):终端输入脚本可正常执行,而加入crontab中无任何输出

使用crontab执行python脚本时&#xff0c;有时会遇到脚本无法执行的问题。这是因为crontab在执行任务时使用的环境变量与我们在终端中使用的环境变量不同。具体来说&#xff0c;crontab使用的环境变量是非交互式(non-interactive)环境变量&#xff0c;而终端则使用交互式(inter…...

计算机网络入门 -- 常用网络协议

计算机网络入门 – 常用网络协议 1.分类 1.1 模型回顾 计算机网络细分可以划为七层模型&#xff0c;分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。而上三层可以划为应用层中。 1.2 分类 1.2.1 应用层 为用户的应用进程提供网络通信服务&#xff0…...

【LabVIEW作业篇 - 4】:属性节点赋值和直接节点赋值的区别体现

文章目录 属性节点赋值和直接节点赋值的区别体现 属性节点赋值和直接节点赋值的区别体现 创建5个圆形指示灯&#xff0c;然后循环点亮&#xff0c;先给圆形指示灯赋值假变量&#xff0c;然后再进行循环。 运行结果&#xff0c;观察结果&#xff0c;发现刚开始运行时&#xff0…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...

在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7

在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤&#xff1a; 第一步&#xff1a; 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为&#xff1a; // 改为 v…...