北风设计模式课程---享元模式

一、总结

一句话总结:

不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例

1、享元模式的本质是什么?

池技术:各种缓存池都是享元模式的体现

说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。

2、享元模式 白话原理?

比如我们每次创建字符串对象时,都需要创建一个新的字符串对象的话,内存开销会很大,所以如果第一次创建了字符串对象“adam“,下次再创建相同的字符串”adam“时,只是把它的引用指向”adam“,这样就实现了”adam“字符串再内存中的共享。

3、享元模式实例?

举个最简单的例子,网络联机下棋的时候,一台服务器连接了多个客户端(玩家),如果我们每个棋子都要创建对象,那一盘棋可能就有上百个对象产生,玩家多点的话,因为内存空间有限,一台服务器就难以支持了,所以这里要使用享元模式,将棋子对象减少到几个实例。下面给出享元模式的定义。

4、享元模式的内部状态和外部状态 指什么?

内部状态指对象共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变;
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

5、享元模式的内部状态和外部状态 实例?

棋子颜色就是棋子的内部状态
方位坐标就是棋子的外部状态

我们举一个最简单的例子,棋牌类游戏大家都有玩过吧,比如说说围棋和跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色略多一点,但也是不太变化的,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,我们落子嘛,落子颜色是定的,但位置是变化的,所以方位坐标就是棋子的外部状态。

6、为什么这里要用享元模式呢?

可以想象一下,上面提到的棋类游戏的例子,比如围棋,理论上有361个空位可以放棋子,常规情况下每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题。

7、享元模式 池子中的对象如何标识?

用唯一标识码判断:如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储

8、何时使用 享元模式?

1、系统中有大量对象时
2、这些对象消耗大量内存时
3、这些对象的状态大部分可以外部化时

9、享元模式 应用实例?

String常量池
数据库连接池

10、享元模式 优缺点?

大大减少了对象的创建,降低了程序内存的占用,提高效率:优点
提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变:缺点

二、简说设计模式——享元模式

转自或参考:简说设计模式——享元模式
https://www.cnblogs.com/adamjwh/p/9070107.html

一、什么是享元模式

  说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。

  比如我们每次创建字符串对象时,都需要创建一个新的字符串对象的话,内存开销会很大,所以如果第一次创建了字符串对象“adam“,下次再创建相同的字符串”adam“时,只是把它的引用指向”adam“,这样就实现了”adam“字符串再内存中的共享。

  举个最简单的例子,网络联机下棋的时候,一台服务器连接了多个客户端(玩家),如果我们每个棋子都要创建对象,那一盘棋可能就有上百个对象产生,玩家多点的话,因为内存空间有限,一台服务器就难以支持了,所以这里要使用享元模式,将棋子对象减少到几个实例。下面给出享元模式的定义。

  享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象。UML结构图如下:

  其中,Flyweight是抽象享元角色。它是产品的抽象类,同时定义出对象的外部状态和内部状态(外部状态及内部状态相关内容见后方)的接口或实现;ConcreteFlyweight是具体享元角色,是具体的产品类,实现抽象角色定义的业务;UnsharedConcreteFlyweight是不可共享的享元角色,一般不会出现在享元工厂中;FlyweightFactory是享元工厂,它用于构造一个池容器,同时提供从池中获得对象的方法。

  1. Flyweight抽象类

  所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。

 1 public abstract class Flyweight {
 2
 3     //内部状态
 4     public String intrinsic;
 5     //外部状态
 6     protected final String extrinsic;
 7
 8     //要求享元角色必须接受外部状态
 9     public Flyweight(String extrinsic) {
10         this.extrinsic = extrinsic;
11     }
12
13     //定义业务操作
14     public abstract void operate(int extrinsic);
15
16     public String getIntrinsic() {
17         return intrinsic;
18     }
19
20     public void setIntrinsic(String intrinsic) {
21         this.intrinsic = intrinsic;
22     }
23
24 }

  2. ConcreteFlyweight类

  继承Flyweight超类或实现Flyweight接口,并为其内部状态增加存储空间。

 1 public class ConcreteFlyweight extends Flyweight {
 2
 3     //接受外部状态
 4     public ConcreteFlyweight(String extrinsic) {
 5         super(extrinsic);
 6     }
 7
 8     //根据外部状态进行逻辑处理
 9     @Override
10     public void operate(int extrinsic) {
11         System.out.println("具体Flyweight:" + extrinsic);
12     }
13
14 }

  3. UnsharedConcreteFlyweight类

  指那些不需要共享的Flyweight子类。

 1 public class UnsharedConcreteFlyweight extends Flyweight {
 2
 3     public UnsharedConcreteFlyweight(String extrinsic) {
 4         super(extrinsic);
 5     }
 6
 7     @Override
 8     public void operate(int extrinsic) {
 9         System.out.println("不共享的具体Flyweight:" + extrinsic);
10     }
11
12 }

  4. FlyweightFactory类

  一个享元工厂,用来创建并管理Flyweight对象,主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或创建一个实例。

 1 public class FlyweightFactory {
 2
 3     //定义一个池容器
 4     private static HashMap<String, Flyweight> pool = new HashMap<>();
 5
 6     //享元工厂
 7     public static Flyweight getFlyweight(String extrinsic) {
 8         Flyweight flyweight = null;
 9
10         if(pool.containsKey(extrinsic)) {    //池中有该对象
11             flyweight = pool.get(extrinsic);
12             System.out.print("已有 " + extrinsic + " 直接从池中取---->");
13         } else {
14             //根据外部状态创建享元对象
15             flyweight = new ConcreteFlyweight(extrinsic);
16             //放入池中
17             pool.put(extrinsic, flyweight);
18             System.out.print("创建 " + extrinsic + " 并从池中取出---->");
19         }
20
21         return flyweight;
22     }
23 }

  5. Client客户端

 1 public class Client {
 2
 3     public static void main(String[] args) {
 4         int extrinsic = 22;
 5
 6         Flyweight flyweightX = FlyweightFactory.getFlyweight("X");
 7         flyweightX.operate(++ extrinsic);
 8
 9         Flyweight flyweightY = FlyweightFactory.getFlyweight("Y");
10         flyweightY.operate(++ extrinsic);
11
12         Flyweight flyweightZ = FlyweightFactory.getFlyweight("Z");
13         flyweightZ.operate(++ extrinsic);
14
15         Flyweight flyweightReX = FlyweightFactory.getFlyweight("X");
16         flyweightReX.operate(++ extrinsic);
17
18         Flyweight unsharedFlyweight = new UnsharedConcreteFlyweight("X");
19         unsharedFlyweight.operate(++ extrinsic);
20     }
21
22 }

  运行结果如下:

  

  从这个结果我们可以看出来,第一次创建X、Y、Z时,都是先创建再从池中取出,而第二次创建X时,因为池中已经存在了,所以直接从池中取出,这就是享元模式。

二、内部状态和外部状态

  上面享元模式的定义为我们提出了两个要求:细粒度和共享对象。我们知道分配太多的对象到应用程序中将有损程序的性能,同时还容易造成内存溢出,要避免这种情况,用到的就是共享技术,这里就需要提到内部状态和外部状态了。

  因为要求细粒度对象,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。

  内部状态指对象共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变;外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

  我们举一个最简单的例子,棋牌类游戏大家都有玩过吧,比如说说围棋和跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色略多一点,但也是不太变化的,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,我们落子嘛,落子颜色是定的,但位置是变化的,所以方位坐标就是棋子的外部状态。

  那么为什么这里要用享元模式呢?可以想象一下,上面提到的棋类游戏的例子,比如围棋,理论上有361个空位可以放棋子,常规情况下每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题。

三、享元模式的应用

  1. 何时使用

  • 系统中有大量对象时
  • 这些对象消耗大量内存时
  • 这些对象的状态大部分可以外部化时

  2. 方法

  • 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储

  3. 优点

  • 大大减少了对象的创建,降低了程序内存的占用,提高效率

  4. 缺点

  • 提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变

  5. 使用场景

  • 系统中存在大量相似对象
  • 需要缓冲池的场景

  6. 应用实例

  • String常量池
  • 数据库连接池

  7. 注意事项

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

四、享元模式的实现

  应用实例的话,其实上面的模板就已经是一个很好的例子了,类似于String常量池,没有的对象创建后存在池中,若池中存在该对象则直接从池中取出。

  为了更好的理解享元模式,这里再举一个实例,比如接了我一个小型的外包项目,是做一个产品展示网站,后来他的朋友们也希望做这样的网站,但要求都有些不同,我们当然不能直接复制粘贴再来一份,有任希望是新闻发布形式的,有人希望是博客形式的等等,而且因为经费原因不能每个网站租用一个空间。

  其实这里他们需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,这是造成服务器的大量资源浪费。如果整合到一个网站中,共享其相关的代码和数据,那么对于硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源;而对于代码,由于是一份实例,维护和扩展都更加容易。

  那么此时就可以用到享元模式了。UML图如下:

  1. 网站抽象类

1 public abstract class WebSite {
2
3     public abstract void use();
4
5 }

  2. 具体网站类

 1 public class ConcreteWebSite extends WebSite {
 2
 3     private String name = "";
 4
 5     public ConcreteWebSite(String name) {
 6         this.name = name;
 7     }
 8
 9     @Override
10     public void use() {
11         System.out.println("网站分类:" + name);
12     }
13
14 }

  3. 网络工厂类

  这里使用HashMap来作为池,通过put和get方法实现加入池与从池中取的操作。

 1 public class WebSiteFactory {
 2
 3     private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
 4
 5     //获得网站分类
 6     public WebSite getWebSiteCategory(String key) {
 7         if(!pool.containsKey(key)) {
 8             pool.put(key, new ConcreteWebSite(key));
 9         }
10
11         return (WebSite)pool.get(key);
12     }
13
14     //获得网站分类总数
15     public int getWebSiteCount() {
16         return pool.size();
17     }
18
19 }

  4. Client客户端

  这里测试用例给了两种网站,原先我们需要做三个产品展示和三个博客的网站,也即需要六个网站类的实例,但其实它们本质上都是一样的代码,可以利用用户ID号的不同,来区分不同的用户,具体数据和模板可以不同,但代码核心和数据库却是共享的。

 1 public class Client {
 2
 3     public static void main(String[] args) {
 4         WebSiteFactory factory = new WebSiteFactory();
 5
 6         WebSite fx = factory.getWebSiteCategory("产品展示");
 7         fx.use();
 8
 9         WebSite fy = factory.getWebSiteCategory("产品展示");
10         fy.use();
11
12         WebSite fz = factory.getWebSiteCategory("产品展示");
13         fz.use();
14
15         WebSite fa = factory.getWebSiteCategory("博客");
16         fa.use();
17
18         WebSite fb = factory.getWebSiteCategory("博客");
19         fb.use();
20
21         WebSite fc = factory.getWebSiteCategory("博客");
22         fc.use();
23
24         System.out.println("网站分类总数为:" + factory.getWebSiteCount());
25     }
26
27 }

  运行结果如下:

  

  可以看出,虽然我们做了6个网站,但网站分类只有2个。这样基本算是实现了享元模式的共享对象的目的,但想想上面提到的内部状态和外部状态,这里实际上没有体现对象间的不同,只体现了它们的共享部分。

  5. 用户类

  所以我们再加一个用户类,作为网站类的外部状态,并在use()方法中传递用户对象,UML如下:

  下面添加一个User类。

 1 public class User {
 2
 3     private String name;
 4
 5     public User(String name) {
 6         this.name = name;
 7     }
 8
 9     public String getName() {
10         return name;
11     }
12
13 }

  然后再对use()方法进行修改,添加参数,以抽象类为例: public abstract void use(User user);

  而客户端中只需对每一个网站添加一个用户即可,如: fx.use(new User("adam"));

  (具体内容可参考源码,源码地址见最下方)

  运行结果如下:

  

  这样就可以协调内部与外部状态,哪怕接手了上千个网站的需求,只要要求相同或类似,实际开发代码也就是分类的哪几种。

  源码地址:https://gitee.com/adamjiangwh/GoF

转载于:https://www.cnblogs.com/Renyi-Fan/p/11029069.html

北风设计模式课程---享元模式相关推荐

  1. 北风设计模式课程---享元模式与单例模式区别

    北风设计模式课程---享元模式与单例模式区别 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 1.享元模式与单例模式区别? 级别:单例模式是类级别的,一个类 ...

  2. 每天一个设计模式之享元模式

    作者按:<每天一个设计模式>旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现.诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式 :) ...

  3. .net设计模式 (享元模式)学习笔记

    运用设计模式只是为了解决一类问题的,当解决掉当前一类问题,通常会在解决这个问题时候 带来其他问题  合理应用扬长避短 结构性设计模式:关注的是类与类之间的关系 .net设计模式 (享元模式)学习笔记 ...

  4. Java设计模式之享元模式(UML类图分析+代码详解)

    大家好,我是一名在算法之路上不断前进的小小程序猿!体会算法之美,领悟算法的智慧~ 希望各位博友走过路过可以给我点个免费的赞,你们的支持是我不断前进的动力!! 加油吧!未来可期!! 本文将介绍java设 ...

  5. 详解设计模式:享元模式

    享元模式(Flyweight Pattern),是对象池的一种体现,也是 GoF 的 23 种设计模式中的一种结构型设计模式. 享元模式 主要用于减少创建对象的数量,以减少内存占用和提高性能.它提供了 ...

  6. 设计模式之享元模式详解

    设计模式之享元模式详解 概述 享元模式定义: ​ 运用共享技术来有效地支持大量细粒度对象的复用.它==通过共享已经存在的对象来大幅度减少需要创建的对象数量==.避免大量相似对象的开销,从而提高系统资源 ...

  7. 【设计模式】享元模式(C#)

    [设计模式]享元模式 1.概述 Flyweight Design Pattern,结构型模式.享元模式中的"享元"指被共享的单元.享元模式通过复用对象,以达到节省内存的目的. 用于 ...

  8. Java设计模式之享元模式

    Java设计模式之享元模式 1. 享元模式概述 1.1 享元定义 1.2 享元模式注意事项 2. 享元模式实现 1. 享元模式概述 1.1 享元定义 1)享元模式(享元模式)也叫蝇量模式:利用共享技术 ...

  9. 设计模式之享元模式(Flyweight)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

最新文章

  1. .Net MVC3中取得当前区域的名字(Area name)
  2. 详解js中typeof、instanceof与constructor
  3. C# 往excel出力数据
  4. 浅谈MySQL数据库中的锁与事务
  5. 设计模式学习(三)——装饰器模式
  6. 烟囱加固技术不断完善
  7. python文件读写到list_Python文件读写
  8. Atitit 视图状态ViewState)的原理与管理
  9. Atitit.编程语言新特性 通过类库框架模式增强 提升草案 v3 q27
  10. windows 下载 gcc
  11. 安科瑞配电室综合监控系统实现配电室内环境的在线监测,保障配电室设备的安全运行
  12. 哈工大本部2022形式语言与自动机期末试题
  13. linux 删除用户 currently logged in,Linux执行usermod时提示user xxx is currently logged in
  14. STM32F103实现OV7725拍照存储为BMP位图
  15. 谷歌--高德地图定位失败geolocation time out
  16. 基于STC89C51单片机,CH340芯片的下载电路
  17. 根据判断PC浏览器类型和手机屏幕像素自动调用不同CSS
  18. 玩转Django2.0---Django笔记建站基础九(一)
  19. 002. 顺应自然,人才会活的好
  20. 实例演示如何在公共互联网构建overlay实现SDWAN

热门文章

  1. 【原创】大叔经验分享(25)hive通过外部表读写hbase数据
  2. raid1 raid2 raid5 raid6 raid10的优缺点和做各自raid需要几块硬盘
  3. 彻底弄懂css3的flex弹性盒模型
  4. python yield用法举例说明
  5. linux下51单片机开发解决方案
  6. movielens推荐系统_浅谈推荐系统+3个小时上手python实现(完整代码)
  7. python语言编写一个生成九宫格图片的代码_用Python一键生成炫酷九宫格图片,火了朋友圈...
  8. getprofile()获取不到路径_做一款RPG游戏,几乎都会用到的Python坐标最短路径计算...
  9. ODPS主备集群双向数据复制导致主备中心网络打爆问题
  10. Flagger on ASM——基于Mixerless Telemetry实现渐进式灰度发布系列 2 应用级扩缩容