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

news2024/9/20 19:36:06

目录

      • 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 纵坐标
           */
          @Override
          public 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 + "]图片,耗时……");
           }
       
           @Override
           public 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 + "]图片,耗时…");
           }
       
           @Override
           public 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 + "]图片,耗时……");
           }
       
           @Override
           public 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
  • “享元”的理念其实就是萃取事物的本质
  • 将对象的内蕴状态与外蕴状态剥离开来,其中内蕴状态成为真正的“元”数据,而外蕴状态则被抽离出去由外部负责维护

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

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

相关文章

Training for Stable Diffusion

Training for Stable Diffusion 笔记来源&#xff1a; 1.Denoising Diffusion Probabilistic Models 2.最大似然估计(Maximum likelihood estimation) 3.Understanding Maximum Likelihood Estimation 4.How to Solve ‘CUDA out of memory’ in PyTorch 1.1 Introduction 训…

FastBee物联网开源项目本地启动调试

一、本地环境准备 &#xff08;1&#xff09;Visual Studio Code&#xff08;启动前端项目&#xff09; &#xff08;2&#xff09;IntelliJ IDEA Community Edition &#xff08;启动后端项目&#xff09; &#xff08;3&#xff09;Navicat或者DBeaver&#xff08;用来操…

Godot学习笔记2——GDScript变量与函数

目录 一、代码编写界面 二、变量 三、函数 四、变量的类型 Godot使用的编程语言是GDS&#xff0c;语法上与python有些类似。 一、代码编写界面 在新建的Godot项目中&#xff0c;点击“创建根节点”中的“其他节点”&#xff0c;选择“Node”。 点击场景界面右上角的绿色…

【STM32】按键控制LED光敏传感器控制蜂鸣器(江科大)

一、按键控制LED LED.c #include "stm32f10x.h" // Device header/*** 函 数&#xff1a;LED初始化* 参 数&#xff1a;无* 返 回 值&#xff1a;无*/ void LED_Init(void) {/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENAB…

此扩展在此工作区中被禁用,因为其被定义为在远程扩展主机中运行。

使用VScode打开代码时&#xff0c;无法跳转函数&#xff0c;不提示报错。 安装python时显示&#xff0c; 此扩展在此工作区中被禁用&#xff0c;因为其被定义为在远程扩展主机中运行。 解决方法&#xff1a; CtrlShiftP &#xff1a;键入trust &#xff0c;工作区&#xff…

空间计算新时代:Vision Pro引领AR/VR/MR市场变革

随着2024年第二季度的结束&#xff0c;空间计算领域的市场动态愈发引人关注。根据国际数据公司&#xff08;IDC&#xff09;的最新报告&#xff0c;我们见证了行业格局的重大变化&#xff0c;尤其是苹果Vision Pro的突出表现&#xff0c;以及AR/VR/MR设备市场的整体趋势。以下是…

LabVIEW软件开发的雷区在哪里?

在LabVIEW软件开发中&#xff0c;有几个需要注意的雷区&#xff0c;以避免常见的错误和提高开发效率&#xff1a; 1. 不良的代码结构 雷区&#xff1a;混乱的代码结构和不清晰的程序逻辑。 后果&#xff1a;导致难以维护和调试的代码&#xff0c;增加了错误和故障的风险。 …

无人机侦察:二维机扫雷达探测设备技术详解

二维机扫雷达探测设备采用机械扫描方式&#xff0c;通过天线在水平方向和垂直方向上的转动&#xff0c;实现对目标空域的全方位扫描。雷达发射机发射电磁波信号&#xff0c;遇到目标后产生反射&#xff0c;反射信号被雷达接收机接收并处理&#xff0c;进而得到目标的位置、速度…

搜维尔科技:【研究】动作捕捉加速游戏开发行业的发展

动作捕捉加速游戏开发行业的发展 Sunjata 的故事始于 2004 年&#xff0c;它将席卷乌干达视频游戏行业&#xff0c;然后席卷全世界。但首先&#xff0c;Klan Of The Kings 的小团队需要工具来实现他们的愿景。 漫画家兼非洲民间传说爱好者罗纳德卡伊马 (Ronald Kayima) 在将…

怎样在 PostgreSQL 中进行用户权限的精细管理?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 怎样在 PostgreSQL 中进行用户权限的精细管理&#xff1f;一、权限管理的重要性二、PostgreSQL 中的权…

[解决方法]Request failed with status code 500错误之一

在写项目时访问后端api时我的axios拦截器进入了错误 然后去浏览器搜索&#xff0c;但是大部分都是因为axios参数或参数格式问题导致的&#xff0c;然而在访问api的编写没有任何问题&#xff0c;后来我反复检查&#xff0c;发现是我写前后端写混了&#xff0c;我把express的 Co…

学习大数据DAY20 Linux环境配置与Linux基本指令

目录 Linux 介绍 Linux 发行版 Linux 和 Windows 比较 Linux 就业方向&#xff1a; 下载 CentOS Linux 目录树 Linux 目录结构 作业 1 常用命令分类 文件目录类 作业 2 vim 编辑文件 作业 3 你问我第 19 天去哪了&#xff1f;第 19 天在汇报第一阶段的知识总结&#xff0c;没什…

深入浅出WebRTC—GCC

GoogCcNetworkController 是 GCC 的控制中心&#xff0c;它由 RtpTransportControllerSend 通过定时器和 TransportFeedback 来驱动。GoogCcNetworkController 不断更新内部各个组件的状态&#xff0c;并协调组件之间相互配合&#xff0c;向外输出目标码率等重要参数&#xff0…

汽车及零部件研发项目管理系统:一汽东机工选择奥博思 PowerProject 提升研发项目管理效率

在汽车行业中&#xff0c;汽车零部件的研发和生产是一个关键的环节。随着汽车市场的不断扩大和消费者需求的不断增加&#xff0c;汽车零部件项目管理的重要性日益凸显。通过有效的项目管理方法及利用先进的数字项目管理系统&#xff0c;可以大幅提高项目的成功率和顺利度&#…

WebRTC QOS方法十三.1(TimestampExtrapolator接收时间预估)

一、背景介绍 虽然我们可通过时间戳的差值和采样率计算出发送端视频帧的发送节奏&#xff0c;但是由于网络延迟、抖动、丢包&#xff0c;仅知道视频发送端的发送节奏是明显不够的。我们还需要评估出视频接收端的视频帧的接收节奏&#xff0c;然后进行适当平滑&#xff0c;保证…

关于 Qt输入法在arm特定的某些weston下出现调用崩溃 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140423667 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

C#知识|账号管理系统-修改账号按钮功能的实现

哈喽,你好啊,我是雷工! 前边学习了通过选择条件查询账号的功能: 《提交查询按钮事件的编写》 本节继续学习练习C#,今天练习修改账号的功能实现。 以下为学习笔记。 01 实现功能 ①:从查询到的账号中,选择某一账号,然后点击【修改账号】按钮,将选中的信息获取显示到…

攻防世界 re新手模式

Reversing-x64Elf-100 64位ida打开 看if语句&#xff0c;根据i的不同&#xff0c;选择不同的数组&#xff0c;后面的2*i/3选择数组中的某一个元素&#xff0c;我们输入的是a1 直接逆向得到就行 二维字符数组写法&#xff1a;前一个是代表有几个字符串&#xff0c;后一个是每…

《蔚蓝档案》模拟器联动皮肤H5+KOC

《蔚蓝档案》模拟器联动皮肤H5KOC 《蔚蓝档案》自上线以来老师们与MuMu模拟器的共同历程&#xff0c;重温难忘瞬间&#xff0c;回忆游戏历程。蔚蓝档案一周年模拟器联动主题皮肤福利&#xff0c;于7月18日-8月16日&#xff0c;在MuMu模拟器搜索【蔚蓝档案联动】进入活动页面&a…

离散数学,半群性质的证明,群,群的性质,子群

目录 1.半群性质的证明 半群的性质 定理5-3.2证明 定理5-3.3证明 半群的性质 定理5-3.4证明 例子 2.群 群是每个元素都可逆的独异点 例子 有限群&#xff0c;阶数&#xff0c;无限群&#xff0c;平凡群 3.群的性质 群中不可能有零元 群中任一元素逆元…