技术成神之路:设计模式(十四)享元模式

news2024/11/16 5:45:09

介绍

享元模式(Flyweight Pattern)是一种结构性设计模式,旨在通过共享对象来有效地支持大量细粒度的对象。

1.定义


享元模式通过将对象状态分为内部状态(可以共享)和外部状态(不可共享),来减少内存使用和提高性能。

2. 主要作用


  • 降低内存消耗
  • 提高性能
  • 共享相似对象

3. 解决的问题


当程序中存在大量相似对象时,使用享元模式可以有效减少内存占用,避免重复对象的创建。

4. 模式原理


包含角色:

  1. Flyweight: 抽象享元类,定义了享元对象的接口。
  2. ConcreteFlyweight: 具体享元类,实现了抽象享元类的接口,负责存储内部状态。
  3. FlyweightFactory: 享元工厂类,用于创建和管理享元对象,确保共享。

UML类图:
在这里插入图片描述
示例:
模拟一个图形绘制的场景,其中使用享元模式共享相同的图形对象(如圆):

// 享元接口
interface Shape {
    void draw(String color);
}

// 具体享元类
class Circle implements Shape {
    private String color;

    public Circle(String color) {
        this.color = color;
        System.out.println("Creating Circle of color: " + color);
    }

    @Override
    public void draw(String color) {
        System.out.println("Drawing Circle of color: " + color);
    }
}

// 享元工厂类
class ShapeFactory {
    private static final Map<String, Shape> circleMap = new HashMap<>();

    public static Shape getCircle(String color) {
        Circle circle = (Circle) circleMap.get(color);
        if (circle == null) {
            circle = new Circle(color);
            circleMap.put(color, circle);
        }
        return circle;
    }
}

// 使用
public class FlyweightPatternDemo {
    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();

        // 共享相同颜色的圆
        Shape circle1 = shapeFactory.getCircle("Red");
        circle1.draw("Red");

        Shape circle2 = shapeFactory.getCircle("Green");
        circle2.draw("Green");

        Shape circle3 = shapeFactory.getCircle("Red");
        circle3.draw("Red");

        System.out.println("Total Circles created: " + ShapeFactory.circleMap.size());
    }
}

打印输出:

Creating Circle of color: Red
Drawing Circle of color: Red
Creating Circle of color: Green
Drawing Circle of color: Green
Drawing Circle of color: Red
Total Circles created: 2

🆗,从这个简单的示例,你会发现享元模式其实很简单,就是共享对象 复用对象,一般开发中并不常用。

在安卓中 HandlerMessage 就使用了享元模式,因为在安卓中 几乎所有事件驱动都是通过Message来进行的,可以说它无处不在,这时候就可以使用享元模式以优化内存使用和提高性能。

下面就以 Message 为例,从源码角度剖析其实现原理!

Message 的池化机制
在 Message 类中,有一个静态的对象池,用于存放可重用的 Message 实例。

    public static final Object sPoolSync = new Object();
    private static Message sPool;//这是一个链表的头指针,指向池中可重用的 Message 对象
    private static int sPoolSize = 0;//记录池中当前的对象数量。

    private static final int MAX_POOL_SIZE = 50;//定义池的最大容量。

    private static boolean gCheckRecycle = true;//一个标志位,用于检查回收的消息是否有效。

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    @UnsupportedAppUsage
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

这里所谓的池 并不是一个集合容器 而是 Message 对象通过 next 属性构成一个链表。每个 Message 对象可以指向下一个可用的 Message,实现了简单的对象池。

当对象不再需要时(如在处理完消息后),调用recycle()可以将其放回池中(通过设置 next 指向池头 sPool),这样下次调用 obtain 时就能复用这些对象,而不是每次都新建。

在调用recycle()可以发现 有一个 isInUse()方法判断这个Message是否正在使用,这个其实不用开发者操心的,因为所有的消息都要经过Looper这个 “传送带”,内部自动回收,翻开Looper的源码文件你会发现在loopOnce方法中最后会调用 msg.recycleUnchecked(),如果你进行了某种自定义操作,导致 Message 未能通过正常的 Handler 流程处理,那么你可能需要手动调用 msg.recycle()

简单概括就是:一条由Message 组成的链表,你想用Message时,就从这个链表上 掐掉一个Message来使用,这个掐掉操作就是将m.next = null,当你不再使用时 就将其放回到链表头,操作就是 next = sPoolsPool = this

5. 优缺点


优点:

  1. 节省内存空间。
  2. 提高性能,特别是创建和管理大量对象时。

缺点:

  1. 外部状态管理可能导致系统逻辑混乱。

6. 应用场景


  • 游戏中的角色、场景元素(如树、建筑)等。
  • 文本处理系统中的字符、字体。
  • 大量相似对象需要频繁创建的场景。

7. 总结


享元模式通过共享对象来优化内存使用和性能,适合于需要创建大量相似对象的场景,但设计复杂性和状态管理需要谨慎处理。

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

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

相关文章

为什么我不建议你考CCNA/HCIA这种初级认证

CCNA和HCIA&#xff0c;这两个证书可能是许多刚入门的网络工程师第一个听说的认证。作为入门级网络认证&#xff0c;CCNA是思科公司推出的&#xff0c;提供了网络基础的全面知识&#xff0c;而HCIA则是华为的基础认证&#xff0c;覆盖了网络技术的基本原理。 这些认证的目的是帮…

2023-03 GESP Python一级试卷

一、单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 1.以下不属于计算机输入设备的有&#xff08; &#xff09;。 ​ A&#xff0e;键盘 ​ B&#xff0e;音箱 ​ C&#xff0e;鼠标 ​ D&#xff0e;传感器 2.计算机系统中存储的基本单位用 B 来表示&am…

用友U8二次开发工具KK-FULL-*****-EFWeb使用方法

1、安装: 下一步&#xff0c;下一步即可。弹出黑框不要关闭&#xff0c;让其自动执行并关闭。 2、服务配置&#xff1a; 输入服务器IP地址&#xff0c;选择U8数据源&#xff0c;输入U8用户名及账号&#xff0c;U8登录日期勾选系统日期。测试参数有效性&#xff0c;提示测试通过…

洞悉市场先机,Vatee万腾平台助力企业精准决策

在瞬息万变的商业环境中&#xff0c;每一个市场动向都可能成为企业兴衰的关键。因此&#xff0c;洞悉市场先机&#xff0c;做出精准决策&#xff0c;成为了企业持续发展和保持竞争力的核心要素。Vatee万腾平台&#xff0c;凭借其强大的数据分析能力和智能化技术&#xff0c;正成…

对象检测边界框损失 – 从IOU到ProbIOU

1.概述 目标检测损失函数的选择在目标检测问题建模中至关重要。通常&#xff0c;目标检测需要两个损失函数&#xff0c;一个用于对象分类&#xff0c;另一个用于边界框回归&#xff08;BBR&#xff09;。本文将重点介绍 IoU 损失函数&#xff08;GIoU 损失、DIoU 损失和 CIoU 损…

【补码运算】设x=FEH,当x分别为下列情况时写出其对应的十进制真值(写出计算过程):(1)为原码(2)为补码。

欢迎来到我的技术博客&#xff01; &#x1f389; 这里不仅有满满的编程干货和学习资源&#xff0c;我的某站账号也为你准备了更多实用的技术视频和知识分享。 &#x1f449; 点击关注我的小破站账号&#xff0c;获取更多编程技巧和学习资源&#xff01; 小破站主页 题目&…

搜索引擎onesearch3实现解释和升级到Elasticsearch v8系列(三)-文档

文档 文档服务负责写入&#xff0c;包括批量&#xff1b;id获取文档&#xff1b;nested写入 写入文档 写入文档主要是构建IndexRequest&#xff0c;索引请求 Elasticsearch v8构建文档索引请求简单很多&#xff0c;可以直接接受Map数据 批量写入文档 批量操作可以融合增删改…

肥胖成因:饮食之外,消耗吸收慢是关键因素

肥胖问题一直被现代社会所关注&#xff0c;不可否认&#xff0c;饮食是影响胖瘦的重要因素之一。高热量、高油脂的食物摄入过多&#xff0c;也确实会导致热量油脂过剩&#xff0c;堆积储存进身体内进而养肥身体。可在正常情况中&#xff0c;就算是消耗吸收率一般的人&#xff0…

从源码看透 Ptmalloc:堆内存分配与释放的背后

&#x1f6f8;0. 前言 内存管理是个很复杂的东西&#xff0c;一般的程序员在写应用层代码的时候根本就不会关注内存的使用&#xff0c;尤其是 GC 语言自带垃圾回收机制&#xff0c;很多同学都使用过 C 语言中的 malloc&#xff0c;但很少有人能知道 malloc 其实是个很复杂的实现…

tomcat服务搭建部署ujcms网站

tomcat服务搭建部署ujcms网站 关闭selinux和防火墙 setenforce 0 && systemctl stop firewalld安装java环境 #卸载原有java8环境 yum remove java*#上传java软件包&#xff0c;并解压缩 tar -xf openjdk-11.0.1_linux-x64_bin.tar.gz && mv jdk-11.0.1 jdk11…

Android Perfetto 学习

1、如何抓取性能日志 方式1、通过手机里的System Tracing抓取 1、点击Settings->System->Developer options->System Tracing->Record trace 打开 2、操作完成后&#xff0c;点击Settings->System->Developer options->System Tracing->Record trace…

C++自动寻径算法

测试 #include <iostream> #include "source/AStar.hpp"int main() {AStar::Generator generator;generator.setWorldSize({25, 25});generator.setHeuristic(AStar::Heuristic::euclidean);generator.setDiagonalMovement(true);generator.addCollision({1, …

Vue 实现高级穿梭框 Transfer 封装

文章目录 01 基础信息1.1. 技术栈1.2. 组件设计a. 竖版设计稿b. 横版设计稿 02 技术方案&#xff08;1&#xff09;初定义数据&#xff08;2&#xff09;注意事项&#xff08;3&#xff09;逻辑草图 03 代码示例3.1. 组件使用3.2. 组件源码./TransferPlus/index.vue./TransferP…

爬虫----webpack

目录 一. 什么是webpack 出现的原因&#xff1a;同名函数 概念: 特征&#xff1a;大量缩进 webpack的格式 简单的webpack格式&#xff1a; 详细的webpack格式&#xff1a; 几个参数的运用 1. webpack数组形式 2. webpack对象格式 3.多个js文件打包 打印要扣的代码 …

【STM32 Blue Pill编程实例】-手机通过HC-05串口蓝牙控制LED

手机通过HC-05串口蓝牙控制LED 文章目录 手机通过HC-05串口蓝牙控制LED1、HC-05串口蓝牙模块介绍2、硬件准备和接线3、模块配置4、代码实现5、手机控制在本文中,我们介绍如何使用 STM32CubeIDE 和 HAL 库将 HC-05 蓝牙模块与 STM32 Blue Pill 开发板连接。 我们将使用 Android…

HarmonyOS 应用获取公钥和 MD5 指纹签名信息

鸿蒙版本获取 MD5 指纹和公钥可参考如下方式; 首先,通过 AGC 官网 将所需证书下载至本地; 其次,通过记事本或者文本编译器的方式将其正式打开,将其内容中前两项 BEGIN CERTIFICATE 和 END CERTIFICATE 的段落删除,仅保留最后一段中的内容(包括 BEGIN CERTIFICATE 和 END CERTI…

Jboss CVE-2017-12149 靶场攻略

漏洞简述 该漏洞为 Java反序列化错误类型&#xff0c;存在于 Jboss 的 HttpInvoker 组件中的 ReadOnlyAccessFilter过滤器中。该过滤器在没有进⾏任何安全检查的情况下尝试将来⾃客户端的数据流进⾏反序列化&#xff0c;从⽽导 致了漏洞 漏洞范围 JBoss 5.x/6.x 环境搭建 …

Xinstall全链路解决方案,让社交App推广效果倍增!

随着互联网的飞速发展&#xff0c;社交App如雨后春笋般涌现&#xff0c;然而&#xff0c;如何在激烈的市场竞争中脱颖而出&#xff0c;成为每个推广者亟待解决的问题。今天&#xff0c;就让我们来揭秘Xinstall如何助力社交App打破运营推广瓶颈&#xff0c;让你的推广之路更加顺…

前端vue-父传值给儿子或者孙子(依赖注入),只能从上层传给下层组件

不是响应式的 加上computed包裹就是响应式的&#xff0c;但是发明这个传值方法的目的就是不让他成为响应式的&#xff0c;所以推荐使用上面的写法。

对商品分类系统的若干问题的思考

科学研究的目的就是研究事物的特征&#xff0c;并根据共同的特征加以分类 商品分类是商业&#xff0c;制造业中最普遍的活动&#xff0c;几乎所有的企业&#xff0c;电商平台都要对销售的商品&#xff0c;使用的原材料&#xff08;BOM&#xff09;进行分类和编号。 商品分类貌似…