设计模式-享元模式

2023-11-20

一、概念

如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用都创建新的对象。目的是提高系统性能。

上面的概念乍一听好像单例模式其实不是,单例模式只保存一个对象,但是这里可以有很多个不同对象,但是每个对象只有一个实例而已。也就是说享元模式使用了单例模式。

例子解释

张三去借书,然后阅读完了还回去了,过一段时间发现还是不懂,又去借了同样的书,但是这本书其实和上一次借的书是同一本。李四也去借书,发现书架上没有,就去图书管理员那边拿出来了一本全新的书。这就是享元模式。

享元模式的主要角色由享元工厂、抽象享元、具体享元类几部分组成。

我们使用这个例子以类图的角度来观察一下:

 

从上面这个例子我们可以看到,这里其实有三个角色:

(1)享元工厂(Llibrary):用于创建具体享元类,维护相同的享元对象。当请求对象已经存在时,直接返回对象,不存在时,在创建对象。在例子中的解释就是图书馆,保存了所有的书,当学生借书时,有就拿走,没有买一本新书。这里面其实是使用了单例模式的。

(2)抽象享元(Book):定义需要共享的对象业务接口。享元类被创建出来总是为了实现某些特定的业务逻辑.

(3)具体享元(ConcreteBook):实现抽象享元类的接口,完成某一具体逻辑。在这里表示可以被借出。
在这里享元工厂是享元模式的核心,它需要确保系统可以共享相同的对象。它会维护一个对象列表,当我们想要获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类:若没有,则创建一个新的享元对象,并将它加入到维护队列中。

二、代码实现

第一步:定义抽象享元类(Book)

public abstract class Book {
 
    //外部状态
    public String name;
 
    public Book(String name){
        this.name = name;
    }
    /**
     * 借书动作
     */
    public abstract void borrow();
}

第二步:定义具体享元类(ConcreteBook)

public class ConcreteBook extends Book {
 
 
    //被借出的书名
    public ConcreteBook(String name){
        super(name);
    }
 
    @Override
    public void borrow() {
        System.out.println(getStuName()+"借了一本书名为:"+this.name);
    }
}

第三步:享元工厂(Llibrary)

public class Library {
 
    //图书馆维护一个图书列表
    private Map<String,Book> bookPools = new HashMap<>();
 
    private static Library factory = new Library();
    //图书馆只有一个
    public static Library getInstance(){
        return factory;
    }
 
    //图书馆外借图书
    public Book libToBorrow(String bookName){
        Book order = null;
        //如果书架有书,直接借出
        if (bookPools.containsKey(bookName)){
            order = bookPools.get(bookName);
        }
        //如果没有,那就拿本新书
        else{
            //根据外部状态创建享元对象
            order = new ConcreteBook(bookName);
            //放到池中
            bookPools.put(bookName,order);
        }
        return order;
    }
 
    //书架上书的数量
    public int getAllBookSize(){
        return bookPools.size();
    }
}

第四步:模拟学生去借书

public class Student {
 
    //图书馆书架上的书
    private static List<Book> books = new ArrayList<Book>();
    private static Library library;
 
    public static void main(String[] args) {
 
        library = Library.getInstance();
        studentBorrow("java编程思想");
        studentBorrow("java核心卷一");
        studentBorrow("java从入门到精通");
        //把每一本书借出去
        for (Book book : books) {
            book.borrow();
        }
        System.out.println("后两本没学会,又借了一次 ");
        studentBorrow("java核心卷一");
        studentBorrow("java从入门到精通");
        Book java核心卷一 = library.libToBorrow("java核心卷一");
        java核心卷一.borrow();
        Book java从入门到精通 = library.libToBorrow("java从入门到精通");
        java从入门到精通.borrow();
 
        //输出一些学生一共借多少本书
        System.out.println("学生一共借了 " + books.size() + " 本书! ");
        //输出一下图书馆一共借出多少本书
        System.out.println("图书馆实际借出" + library.getAllBookSize() + " 本书");
    }
 
 
    private static void studentBorrow(String bookName){
        books.add(library.libToBorrow(bookName));
    }
 
}

在上面其实学生一共借了5次书,但是有两本是重复的,所以对于图书馆来说,其实是借出去了三本书。

输出结果:

借了一本书名为:java编程思想
 
借了一本书名为:java核心卷一
 
借了一本书名为:java从入门到精通
 
后两本没学会,又借了一次 
 
借了一本书名为:java核心卷一
 
借了一本书名为:java从入门到精通
 
学生一共借了 5 本书! 
 
图书馆实际借出3 本书

三、享元模式在java中的体现

1.String中的intern()方法

采用new 创建的字符串对象不进入字符串池

字符串相加的时候,都是静态字符串的结果会添加到字符串池,如果其中含有变量则不会进入字符串池中

intern()有两个作用,第一个是将字符串字面量放入常量池(如果池没有的话),第二个是返回这个常量的引用

public static void main(String[] args) {
        String str1 = "和谐";
        String str2 = "社会";
        String str3 = "和谐社会";
        String str4;
        str4 = str1 + str2;
        System.out.println(str3 == str4);
//        如果是String的对象池中有该类型的值,则直接返回对象池中的对象
        str4 = (str1 + str2).intern();
        System.out.println(str3 == str4);
}

输出结果:false true

2.Integer中的vauleOf()方法

在范围[-128, 127]内在Integer缓存中直接取,范围外通过new Integer(i) 生成

            //Integer中的缓存数组 cache
			cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
 
			public static Integer valueOf(int i) {
                if (i >= IntegerCache.low && i <= IntegerCache.high)
                    return IntegerCache.cache[i + (-IntegerCache.low)];
                return new Integer(i);
       	 }
        Integer i1 = Integer.valueOf(99);
        Integer i2 = Integer.valueOf(99);
        System.out.println(i1==i2);
        Integer i3 = Integer.valueOf(128);
        Integer i4 = Integer.valueOf(128);
        System.out.println(i3==i4);

输出结果:true false

四、享元模式特点

1、优点
(1)节省内存空间,对于可重复的对象只会被创建一次,对象比较多时,就会极大的节省空间。

(2)提高效率,由于创建对象的数量减少,所以对系统内存的需求也减小,使得速度更快,效率更高。

2、缺点
提高了系统复杂性,需要分离出外部状态(k)和内部状态(v),而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱,为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。

● 内部状态

内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,它们可以作为一个对象的动态附加信息,不必直接储存在具体某个对象中,属于可以共享的部分。

● 外部状态

外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态,它是一批对象的统一标识,是唯一的一个索引值。

3、享元模式与单例模式的区别
(1)享元设计模式是一个类有很多对象,而单例是一个类仅一个对象。

(2)享元模式是为了节约内存空间,提升程序性能,而单例模式则主要是出于共享状态的目的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式-享元模式 的相关文章

  • 使用windeployqt与inno setup实现windows下Qt程序发布打包

    一 使用windeployqt拷贝依赖文件 在发布生成的exe程序时 需要复制一大堆dll 如果自己去复制dll 很可能丢三落四 导致exe在别的电脑里无法正常运行 因此Qt官方开发环境里自带了一个工具 windeployqt exe 1
  • HR 宏

    表TRMAC 程序DBPNPMAC 转载于 https www cnblogs com CtrlS p 10818285 html

随机推荐

  • Tableau 中的组(group)与集(set)

    使用tableau也好长时间了 最近有同事问我tableau中组和集有什么区别 那今天就谈谈我个人对组和集的一些理解 也算是一个回顾 理解不对之处还望大牛们及时指正 1 首先看看官方的定义 组是构成更高级别类别的维度成员的组合 单独理解的确
  • Error:(923) Apostrophe not preceded by \ (in %1$s's )

    问题描述 在使用Android的strings xml时 英文版会遇到许多简写 其中 s 和 t时经常使用的 如果直接使用编译无法通过就报标题所示错误 解决方法 主要原因是 是特殊字符 需要转义 加 既可解决 示例 xx s thing 修
  • VMware 中搭建 SylixOS 环境

    1 制作 x86 平台 U 盘启动盘 详细步骤见 RealEvo IDE 使用手册 第八章 制作成功后插入 U 盘 2 创建 VMware 虚拟机设备 打开 VMware 这里使用版本为 15 5 6 点击 创建新的虚拟机 按如下步骤创建虚
  • linux 如何查看进程端口号,在linux中查看进程占用的端口号

    在Linux 上的 etc services 文件可以查看到更多关于保留端口的信息 可以使用以下六种方法查看端口信息 ss 可以用于转储套接字统计信息 netstat 可以显示打开的套接字列表 lsof 可以列出打开的文件 nmap 是网络
  • srand(time(NULL))

    srand函数是随机数发生器的初始化函数 原型 void srand unsigned seed 用法 它初始化随机种子 会提供一个种子 这个种子会对应一个随机数 如果使用相同的种子后面的rand 函数会出现一样的随机数 如 srand 1
  • 什么是页面文件使用率

    你好 很高兴能看到你的问题 也很高兴我能够回答你的问题 你提问 什么是页面文件使用率 首先我们必须要了解什么叫 页面文件 页面文件是一个存放在硬盘上的文件 大多数情况下都放在系统磁盘 如C 盘 的根目录下 这个文件不允许用户访问 只能够被操
  • 简单的文件内容繁简体甄别

    在做国际化的时候 很多旧文件中的简体或者繁体 需要优化 一个一个找很麻烦 于是在查阅资料后 自己编写了一个简单的工具 废话不多说 上码子 插件 mui ui vue js jquery 項目是 hbuildx 直接創建的 change la
  • pip相关命令

    查看当前pip源 pip config list 更改pip源 pip config set global index url 清华源网址 升级pip python m pip install upgrade pip 安装模块 pip in
  • vue3中路由的使用

    路由是什么 vue中的路由是用来管理页面切换或跳转的一种方式 Vue Router是vue官方的路由管理器 1 Vue Router的安装 需要先弄好npm npm install vue router 4 s 在安装完成之后 开始尝试简单
  • BT蓝牙协议 — HFP/HSP的关联与区别

    一 前言 有时 我们能看到有的蓝牙产品标明支持HFP HSP 而有的产品却只标注了支持HFP 那么HFP or HSP是什么呢 又有什么样的关系呢 二 HSP协议 HSP Headset Profile 耳机模式 仅实现了最基本的通话操作
  • c语言判断一个数是否为偶数

    include
  • 图解五种磁盘调度算法, FCFS, SSTF, SCAN, C-SCAN, LOOK

    一 FCFS 调度 先来先服务 磁盘调度的最简单形式当然是先来先服务 FCFS 算法 虽然这种算法比较公平 但是它通常并不提供最快的服务 例如 考虑一个磁盘队列 其 I O 请求块的柱面的顺序如下 98 183 37 122 14 124
  • 华为OD机试 Python 【TLV解析】

    题目 题目简述 你收到了一串由两端设备传递的TLV格式的消息 现在你需要根据这串消息生成一个对应的 tag length valueOffset 列表 详细说明 这串消息其实是由许多小组成的 每一小组里包含了tag length value
  • win可以上网,但是右下方显示“无internet链接“

    使用了下面链接的方法 成功解决 Win10可以联网但右下角显示无法连接到Internet怎办 首先 打开控制面板 control 右上角 将查看方式切换为小图标 调整计算机的设置下 找到并点击网络和共享中心 网络和共享中心窗口 左侧点击更改
  • 苹果鼠标win10不能滑动_解决WIN10使用苹果鼠标滚轮不能使用的问题

    这个花费了蛮多时间却解决不了 网上流行各种各样的尝试 还有很多的安装包 都试了一遍 无一解决 绝望的时候 看到有个网友发的云盘链接 感谢 花小柏 一安装即可使用 太感谢了 最后也分享给大家 链接 https pan baidu com s
  • Linux运维脚本

    20200911 这里记录一些平时使用的脚本 免密登陆什么的 免密登陆 bin bash f root ssh id rsa pub ssh keygen t rsa P f root ssh id rsa gt dev null expe
  • 【2023版】最新stable diffusion安装教程,一键安装,永久使用,stable diffusion下载安装教程!

    关于现在非常红火的AI绘画 很多感兴趣的人不知道如何入手 如果你的电脑配置足够好 那么不要犹豫 让我来教你如何在本地电脑全免费运行当下最强大的AI绘画工具 Stable Diffusion 吧 一 Stable Diffusion 是什么
  • cmake Targets:CMake如何构建简单的Target

    CMake有三个基本命令 用于定义CMake Target 分别是 add executable 构建exe add library 构建库 add custom target 自定义构建目标在camke构建阶段运行的 add execut
  • go 进阶 go-zero相关: 七. 拦截器与熔断拦截器

    目录 一 拦截器的基础使用 1 服务端拦截器 2 客户端拦截器 二 拦截器底层底层执行原理 三 go zero默认添加的拦截器 客户端 1 熔断器拦截器 BreakerInterceptor 服务端 一 拦截器的基础使用 在go zero
  • 设计模式-享元模式

    一 概念 如果在一个系统中存在多个相同的对象 那么只需要共享一份对象的拷贝 而不必为每一次使用都创建新的对象 目的是提高系统性能 上面的概念乍一听好像单例模式其实不是 单例模式只保存一个对象 但是这里可以有很多个不同对象 但是每个对象只有一