【设计模式】第十一章:享元模式详解及应用案例

news2024/12/25 0:27:32

系列文章

【设计模式】七大设计原则
【设计模式】第一章:单例模式
【设计模式】第二章:工厂模式
【设计模式】第三章:建造者模式
【设计模式】第四章:原型模式
【设计模式】第五章:适配器模式
【设计模式】第六章:装饰器模式
【设计模式】第七章:代理模式
【设计模式】第八章:桥接模式
【设计模式】第九章:外观模式 / 门面模式
【设计模式】第十章:组合模式
【设计模式】第十一章:享元模式
【设计模式】第十二章:观察者模式
【设计模式】第十三章:模板方法模式
【设计模式】第十四章:策略模式
【设计模式】第十五章:责任链模式
【设计模式】第十六章:迭代器模式
【设计模式】第十七章:状态模式
【设计模式】第十八章:备忘录模式
【设计模式】第十九章:访问者模式
【设计模式】第二十章:解释器模式
【设计模式】第二十一章:命令模式
【设计模式】第二十二章:中介者模式


文章目录

  • 系列文章
  • 一、定义
  • 二、角色分类
  • 三、实现方式
    • UML图
    • 单纯享元模式
    • 复合享元模式
  • 四、应用场景
  • 五、优缺点
    • 优点
    • 缺点


一、定义

摘自百度百科: 它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。


二、角色分类

抽象享元类(Flyweight)

通常是一个接口或抽象类,其声明了具体享元类的公共方法,这些方法可以向外界提供享元对象的内部数据或内部状态,同时也可以通过这些方法来设置外部数据或外部状态

具体享元类(Concrete Flyweight)

它实现了抽象享元类,它的实例被称为享元对象;在具体享元类内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。

非共享具体享元类(Unshared Concrete Flyweight)

不能被共享的子类可被设计为非共享具体享元类,当需要一个非共享具体享元类的对象时可以直接通过实例化创建

享元工厂类(Flyweight Factory)

该类主要用于创建和管理享元对象,它针对抽象享元类变成,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储键值对的集合(也可以是其他类型的集合),可以结合工厂模式进行设计

客户角色(Client)

具体调用方法的角色


三、实现方式

UML图

Image.png

单纯享元模式

抽象享元类(Flyweight)

public interface Flyweight {
  public void operate(String type);
}

具体享元类(Concrete Flyweight)和非共享具体享元类(Unshared Concrete Flyweight)

public class CharacterFlyWeight implements Flyweight {
  // 内部状态是不变的,外部状态是变化的
  // 然后通过共享不变的部分,达到减少对象数量并节约内存的目的
  private String name;

  /**
   * 外部状态(非享元)
   * @param name
   */
  public CharacterFlyWeight(String name) {
    this.name = name;
  }

  /**
   * 具体享元+非享元结合
   * @param type
   */
  @Override
  public void operate(String type) {
    System.out.println("姓名 = " + name);
    System.out.println("属性 = " + type);
  }
}

享元工厂(Flyweight Factory)

public class FlyweightFactory {
  // 由工厂方法产生所需要的享元对象
  private Map<String, Flyweight> characterPool = new HashMap<>();

  public Flyweight factory(String user) {
    // 先从缓存中查找对象
    Flyweight flyweight = characterPool.get(user);
    if (null == flyweight) {
      // 如果对象不存在则创建一个新对象
      flyweight = new CharacterFlyWeight(user);
      // 将新对象添加到缓存中
      characterPool.put(user, flyweight);
    }
    return flyweight;
  }
}

客户角色(Client)

public class Client {
  public static void main(String[] args) {
    FlyweightFactory factory = new FlyweightFactory();
    Flyweight flyweightA = factory.factory("小A");
    flyweightA.operate("文静");

    Flyweight flyweightB = factory.factory("小B");
    flyweightB.operate("腼腆");

    Flyweight flyweightC = factory.factory("小A");
    flyweightC.operate("活泼");

    System.out.println(flyweightA == flyweightC); //true
    
  }
}

运行结果

姓名 = 小A
属性 = 文静
姓名 = 小B
属性 = 腼腆
姓名 = 小A
属性 = 活泼
true

复合享元模式

抽象享元角色(Flyweight)

public interface Flyweight {
  public void operate(String type);
}

具体享元角色(Concrete Flyweight)和非共享具体享元角色(Unshared Concrete Flyweight)

public class CharacterFlyweight implements Flyweight {
  // 内部状态是不变的,外部状态变化
  // 通过共享不变的部分,达到减少对象数量并节约内存的目的
  private String name;

  /**
   * 外部状态(非享元)
   * @param name
   */
  public CharacterFlyweight(String name) {
    this.name = name;
  }

  /**
   * 具体享元和非享元结合
   * @param type
   */
  @Override
  public void operate(String type) {
    System.out.println("姓名 = " + name);
    System.out.println("属性 = " + type);
  }
}

复合享元角色(Composite Concrete Flyweight)

public class CharacterCompositeFlyweight implements Flyweight {
  private Map<String, Flyweight> files = new HashMap<>();

  /**
   * 增加一个新的单纯享元对象到集合里
   */
  public void add(String key, Flyweight flyweight) {
    files.put(key, flyweight);
  }

  /**
   * 外部状态作为参数传入到方法中
   */
  @Override
  public void operate(String type) {
    Flyweight flyweight;
    for (String key : files.keySet()) {
      flyweight = files.get(key);
      flyweight.operate(type);
    }
  }
}

享元工厂角色(Flyweight Factory)

public class FlyweightFactory {
  private Map<String, Flyweight> characterPool = new HashMap<>();

  /**
   * 复合工厂方法
   * 一种用于提供单纯享元对象,另一种提供复合享元对象
   */
  public Flyweight factory(List<String> compositeState) {
    CharacterCompositeFlyweight compositeFlyweight = new CharacterCompositeFlyweight();
    compositeState.forEach(any -> compositeFlyweight.add(any, this.factory(any)));
    return compositeFlyweight;
  }

  /**
   * 单纯工厂方法
   * 提供所需要的享元对象
   */
  public Flyweight factory(String user) {
    // 先从缓存中查找对象
    Flyweight flyweight = characterPool.get(user);
    if (null == flyweight) {
      // 如果对象不存在则创建一个新对象
      flyweight = new CharacterFlyweight(user);
      // 将新对象添加到缓存中
      characterPool.put(user, flyweight);
    }
    return flyweight;
  }
  
}

客户角色(Client)

public class Client {
  public static void main(String[] args) {
    List<String> compositeState = new ArrayList<String>();
      compositeState.add("小A");
      compositeState.add("小B");
      compositeState.add("小C");
      compositeState.add("小B");
      compositeState.add("小A");

      FlyweightFactory flyFactory = new FlyweightFactory();
      Flyweight compositeFly1 = flyFactory.factory(compositeState);
      Flyweight compositeFly2 = flyFactory.factory(compositeState);
      compositeFly1.operate("梦游中...");

      System.out.println("---------------------------------");
      System.out.println("复合享元模式是否可以共享对象:" + (compositeFly1 == compositeFly2)); //false

      String user = "小A";
      Flyweight fly1 = flyFactory.factory(user);
      Flyweight fly2 = flyFactory.factory(user);
      System.out.println("单纯享元模式是否可以共享对象:" + (fly1 == fly2)); //true
  }
}

运行结果

姓名 = 小B
属性 = 梦游中...
姓名 = 小A
属性 = 梦游中...
姓名 = 小C
属性 = 梦游中...
---------------------------------
复合享元模式是否可以共享对象:false
单纯享元模式是否可以共享对象:true

四、应用场景

以下部分内容摘自菜鸟教程

意图: 运用共享技术有效地支持大量细粒度的对象。

主要解决: 在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。

何时使用:

  1. 系统中有大量对象。
  2. 这些对象消耗大量内存。
  3. 这些对象的状态大部分可以外部化。
  4. 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
  5. 系统不依赖于这些对象身份,这些对象是不可分辨的。

如何解决: 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。

关键代码: 用 HashMap 存储这些对象。

应用实例:

  1. JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
  2. 数据库的连接池。

使用场景:

  1. 系统有大量相似对象。
  2. 需要缓冲池的场景。

注意事项:

  1. 注意划分外部状态和内部状态,否则可能会引起线程安全问题。
  2. 这些类必须有一个工厂对象加以控制。

五、优缺点

优点

大大减少对象的创建,降低系统的内存,使效率提高。

缺点

提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

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

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

相关文章

每周学点数学 3:概率论基础2

文章目录 1.独立性与相关性2.条件概率与边缘概率3.大数定律与中心极限定理4.随机过程5.概率论的应用 1.独立性与相关性 独立性与相关性是在数据分析中非常重要的两个概念&#xff0c;它们之间存在一定的联系&#xff0c;但也有明显的区别。 独立性&#xff08;Independence&…

CSS(持续更新!~)

二&#xff1a; 进阶&#xff1a; 只打算起到装饰作用的图片就建议就背景图片 块级标签就是&#xff1a;独占一行的标签&#xff08;比如div&#xff09;并且可以加宽加高 行内元素&#xff1a;就是不会独占一行的标签&#xff08;比如a&#xff0c;span等等&#xff0c;不可以…

软件测试为什么要学习数据库

目录 前言&#xff1a; 一、为什么要学习数据库 二、常见数据库 三、如何学习数据库 前言&#xff1a; 数据库是用于存储、组织和管理数据的系统&#xff0c;它在各个领域都得到广泛应用&#xff0c;包括企业、学术界、政府和互联网等。 一、为什么要学习数据库 能够反作…

PS 快速选择工具基本操作讲解 通过 选择并遮住 调整后续

我们先打开PS软件 然后打开一个项目 前面几篇文章我们讲了磁性套索工具 其实就已经比较智能了 但是 毕竟拿东西还得自己去描边&#xff0c;操作起来并不是特别轻松 那么 我们今天看的东西就会更智能一些 我们将鼠标在下图指向位置右键 然后在弹出的选项中选择快速选择工具 选…

Notepad++ 打开单独窗口

应用1、打开完全独立的新窗口 快捷键&#xff1a;AltF6 应用2、打开新视图

CSDN 周赛 61 期

CSDN 周赛 60 期 参赛体验判断题单选题填空题编程题1、题目名称:最近的回文数2、题目名称:风险投资小结参赛体验 嗯,今天的填空题又出了新的幺蛾子,直接所有人不给分?看到 bug 提交去好多人在议论这问题。 这个未阅卷是个啥情况?机器人下班了,要改人工了? 然后,C 站…

小说系统源码分享,打造完整小说生态系统

小说已经成为了现代人娱乐生活的重要组成部分&#xff0c;而现在的小说不仅仅是纸质的&#xff0c;越来越多的人开始阅读网络小说。在这个数字化的时代&#xff0c;打造一个完整的小说生态系统变得尤为重要。本篇文章将为大家分享小说系统源码&#xff0c;帮助大家打造完整的小…

D盘不见了?3个方法,教你找回丢失的d盘!

谁能帮帮我呀&#xff01;电脑使用的好好得&#xff0c;d盘突然就不见了。我还有很多很重要的文件都保存在里面呢&#xff01;还有找回这些文件的希望吗&#xff1f; D盘作为电脑的一个重要磁盘&#xff0c;我们可能会将很多很重要的文件都保存在里面。但不知道大家有没有遇到过…

哈希与位图的结合--布隆过滤器与哈希切分

上一章讲了位图&#xff0c;我们知道了在海量数据中查找一个数是否存在&#xff0c;可以用每一个比特位标识。 但是位图只能处理整数&#xff0c;要是字符串或者其它的呢&#xff0c;位图便无法处理了&#xff0c;这个时候便需要用到布隆过滤器了. 目录 布隆过滤器提出 布隆…

斯坦福发布最新LLM排行榜AlpacaEval,微软WizardLM登顶开源模型第一

斯坦福发布最新LLM排行榜AlpacaEval&#xff0c;微软WizardLM登顶开源模型第一 文章目录 Part 1. 众多LLM排行榜Part 2. AlpacaEval 技术细节2.1 AlpacaEval 评估效果2.2 如何使用AlpacaEval评估模型 Part 3. 微软 WizardLM 登顶开源模型第一3.1 关于 WizadLM 与 Evol-Instruc…

PostgreSQL使用localhost可以连接,使用IP无法连接

问题描述&#xff1a;PostgreSQL使用localhost可以连接&#xff0c;使用IP无法连接 默认情况下&#xff0c;刚安装完成的 postgresSQL12 无法使用 数据库连接工具&#xff08;如postman&#xff09;连接。需要为其修改配置&#xff0c;开放连接权限。 修改pg_hba.conf 增加…

【js小案例】视频倍数播放、计算机、待办事项管理

视频倍数播放示例图&#xff1a; 视频倍数播放代码&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>控制视频播放速度</title> </head> <body><video id"myVideo" width&quo…

c语言内存

程序是保存在硬盘中的&#xff0c;要载入内存才能运行&#xff0c;CPU也被设计为只能从内存中读取数据和指令。 对于CPU来说&#xff0c;内存仅仅是一个存放指令和数据的地方&#xff0c;并不能在内存中完成计算功能&#xff0c; 如&#xff1a;计算abc,必须将a,b,c都读取到CPU…

解锁生成式AI万亿规模市场,亚马逊云科技有效降低AIGC门槛

ChatGPT一声惊雷&#xff0c;让全球见识到了生成式AI的威力。当前&#xff0c;生成式AI进入一个爆发时刻&#xff0c;并在许多领域中展现出它的无限潜力。那么&#xff0c;在这轮生成式AI大爆发中&#xff0c;企业应当如何抓住机遇&#xff0c;顺应这一波时代的潮水&#xff0c…

PHP:数据库中设置文本长度,通过js去限制前台文本长度。扩展:数据类型的限制

效果图 如上图&#xff1a;当测试111的长度超过数据库中限制的长度&#xff0c;进行提示&#xff0c;并且自动将多余部分截掉 HTML代码 <!-- 附加属性 --> <div class"text-nav-1 " id"append1"> <div >append1</div><input…

如何使经纬度标注在图框内部

在生成经纬网格之后&#xff0c;如果标注了经纬度&#xff0c;仔细查看图框边缘&#xff0c;可以看到标注的经纬度出现在了图框的外面&#xff0c;这样显得不是很美观&#xff0c;我们可以通过偏移的方法让其回到图框内部&#xff0c;这里为大家介绍一下具体的操作方法&#xf…

达梦数据库 SQL交互式查询工具打不开问题处理

目录 1、开始菜单找到 “SQL交互式查询工具”。 2、 右键进入 打开文件位置。 3、右键进入属性&#xff0c;找到目标位置 4、进入我的电脑&#xff0c;访问该地址&#xff0c;并授予此地址权限 1、开始菜单找到 “SQL交互式查询工具”。 2、 右键进入 打开文件位置。 3、右…

【ARM Coresight 及 DS-5 介绍 2 - ARM Coresight 介绍】

文章目录 1.1 ARM Coresight 介绍1.1.1 ARM Coresight 发展历史 1.2 ARM Coresight 框架介绍1.1.1 Trace 通路1.1.3 Debug 通路1.1.4 Trigger 通路 1.1 ARM Coresight 介绍 ARM Coresight是ARM公司提供的一种调试和跟踪技术&#xff0c;用于ARM处理器的调试和性能分析。它通过…

根据ABAP字符寻找程序

知识来之不易&#xff0c;还请多点赞&#xff01; SE38执行程序RPR_ABAP_SOURCE_SCAN

实现流程化办公,该说不说还得借力低代码开发框架

在科技的推动下&#xff0c;流程化办公已经成为潮流。如何实现流程化办公&#xff0c;让越来越多的企业打通各部门之间的协作&#xff0c;实现高效率发展&#xff1f;借力低代码开发框架&#xff0c;让那遥不可及的梦想变为现实&#xff0c;跟传统操作方式比起来&#xff0c;低…