享元模式思想  

  就以小时候导锅来说吧(导锅是我家乡的方言,就是用细沙把一个模型锅的形状拓下来,然后把铝水倒进模型中,就导好一个锅了。小时候很喜欢看的,可惜现在看不到了。上个图片回忆下)了解了这个过程后就知道,沙子、模型锅、都是可以一直使用的。而用做化铝水的物品是需要不同铝制品东西化生成。做这个生意的人不可能用一次沙子和模型锅就因为用过不用了。

  在代码的世界里,那些不变的类属性就像这些沙子和模型锅可以共享使用,而变化的类属性就像为导锅化铝水的铝制品一样是不同变化的。但实际上很多程序员就没有共享那些不变属性,而是生成一个类,就新建一个这样的可共享属性。就如同导锅用了一次的锅和沙子就弃之不用了,下次再用新的一样,多么可笑的事情啊,而享元模式就是为了阻止我们干这样可笑的蠢事而生的。

  

  来看下共享模式的定义:运用共享技术有效地支持大量细粒度的对象,达到节省内存的目的。咋一看不太明白这句话的意思,其实就是本文开篇描述的那个意思。

享元模式结构

模式参与成员:

  Flyweight:享元抽象接口

  FlyweightFactory:管理共享享元对象的缓存池。

  ConcreteFlyweight:可共享的类,里面存放着都是不会随着场景变化而变化的状态,同时介绍不可共享的状态的传入。

  Client:模拟试用享元对象的地方(对享元模式来说就是客户端)

例子

  为了更好的来理解享元模式,我们来举个例子。就以网店邮寄快递这个场景为例子展示此模式。

  一个快递对象都有哪些属性呢,来列举下。快递物品名称,价格,邮寄人,邮寄人电话,邮寄人地址,接收人姓名,接收人电话,接收人地址。既然要用享元模式了就要根据此模式的特征来分析一下,可共享(不变)状态和不可共享(随着不同包裹变化的)状态。

不变的状态有:邮寄人,邮寄人电话,邮寄人地址。我们把相同的物品共享一个对象的话,还有物品名称、价格。

不可共享的状态:接收人姓名,接收人电话,接收人地址。

分好这变化状态和不变状态以后,接下来就构建代码了。

享元抽象接口

  1. //抽象接口,相当于 Flyweight
  2. public interface KuaiDi {
  3. // jsr 接受不可共享部分
  4. public void sendkd(Jiesr jsr);
  5. }

实现接口的可共享类

  1. public class ConcreteKuaidi implements KuaiDi {
  2.  
  3. private String kdName;
  4. private String fsrName;
  5. private String fsrPhone;
  6. private String fsrAddress;
  7.  
  8. public ConcreteKuaidi(String name,String fsrName,String fsrdh,String fsradrress){
  9. this.kdName = name;
  10. this.fsrName = fsrName;
  11. this.fsrPhone = fsrdh;
  12. this.fsrAddress = fsradrress;
  13. }
  14.  
  15. /* getter 和 setter 方法省略了 */
  16.  
  17. @Override
  18. public void sendkd(Jiesr jsr) {
  19. // TODO Auto-generated method stub
  20. String msg = "邮寄"+ kdName +"给"+jsr.getJsrName()+"\n";
  21. msg = msg + "接受人电话:"+jsr.getJsrPhone()+"\n";
  22. msg = msg + "接受人地址:"+jsr.getAddress()+"\n";
  23. msg = msg + "发送地址:"+fsrAddress+"\n";
  24. System.out.println(msg);
  25. // 打印类对象地址,更好的看出来类共享
  26. System.out.println(this.toString());
  27. System.out.println("------------------------------------");
  28. }
  29.  
  30. }

不可共享部分的类, 快递接收人

  1. public class Jiesr {
  2. private String jsrName;
  3. private String jsrPhone;
  4. private String Address;
  5.  
  6. public String getJsrName() {
  7. return jsrName;
  8. }
  9.  
  10. public String getJsrPhone() {
  11. return jsrPhone;
  12. }
  13.  
  14. public String getAddress() {
  15. return Address;
  16. }
  17.  
  18. public Jiesr(String jsrName,String jsrPhone,String Address){
  19. this.jsrName = jsrName;
  20. this.jsrPhone = jsrPhone;
  21. this.Address = Address;
  22. }
  23. }

享元工厂,实现成为单例模式,是共享以前生成的对象,还是需要新建一个对象,由这儿决定。

  1. public class KudiFactory {
  2. private static KudiFactory kudiFactory = new KudiFactory();
  3.  
  4. private KudiFactory(){}
  5.  
  6. public static KudiFactory getInstance(){
  7. return kudiFactory;
  8. }
  9. //用于存储 对象
  10. private Map<String,KuaiDi> map = new HashMap<String,KuaiDi>();
  11.  
  12. // 类似工厂,存有对象池,在这儿生成对象
  13. public KuaiDi getKuaidi(String name,String fsrName,String fsrdh,String fsradrress){
  14. KuaiDi kd = map.get(name);
  15. if(kd == null){
  16. kd = new ConcreteKuaidi(name,fsrName,fsrdh,fsradrress);
  17. map.put(name,kd);
  18. }
  19. return kd;
  20. }
  21. }

调用

  1. public class Client {
  2. public static void main(String[] args) {
  3.  
  4. KudiFactory kk = KudiFactory.getInstance();
  5. KuaiDi kuaidi1 = kk.getKuaidi("耐克运动鞋", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
  6. Jiesr jsr1 = new Jiesr("张小四", "", "**省**市张小四村");
  7. kuaidi1.sendkd(jsr1);
  8.  
  9. KuaiDi kuaidi2 = kk.getKuaidi("耐克运动库", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
  10. Jiesr jsr2 = new Jiesr("王小五", "", "**省**市张小5村");
  11. kuaidi2.sendkd(jsr2);
  12.  
  13. KuaiDi kuaidi3 = kk.getKuaidi("耐克运动鞋", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
  14. Jiesr jsr3 = new Jiesr("李小六", "", "**省**市张小6村");
  15. kuaidi3.sendkd(jsr3);
  16.  
  17. KuaiDi kuaidi4 = kk.getKuaidi("耐克运动库", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
  18. Jiesr jsr4 = new Jiesr("钱小七", "", "**省**市张小7村");
  19. kuaidi4.sendkd(jsr4);
  20.  
  21. KuaiDi kuaidi5 = kk.getKuaidi("耐克运动鞋", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
  22. Jiesr jsr5 = new Jiesr("孙小八", "", "**省**市张小8村");
  23. kuaidi5.sendkd(jsr5);
  24.  
  25. }
  26. }
  27.  
  28. /*-------------------------------------执行结果-------------------------------*/
  29. 邮寄耐克运动鞋给张小四
  30. 接受人电话:
  31. 接受人地址:**省**市张小四村
  32. 发送地址:汉东省京州市耐克园区10
  33.  
  34. example.ConcreteKuaidi@5994a1e9
  35. ------------------------------------
  36. 邮寄耐克运动库给王小五
  37. 接受人电话:
  38. 接受人地址:**省**市张小5
  39. 发送地址:汉东省京州市耐克园区10
  40.  
  41. example.ConcreteKuaidi@2d11f5f1
  42. ------------------------------------
  43. 邮寄耐克运动鞋给李小六
  44. 接受人电话:
  45. 接受人地址:**省**市张小6
  46. 发送地址:汉东省京州市耐克园区10
  47.  
  48. example.ConcreteKuaidi@5994a1e9
  49. ------------------------------------
  50. 邮寄耐克运动库给钱小七
  51. 接受人电话:
  52. 接受人地址:**省**市张小7
  53. 发送地址:汉东省京州市耐克园区10
  54.  
  55. example.ConcreteKuaidi@2d11f5f1
  56. ------------------------------------
  57. 邮寄耐克运动鞋给孙小八
  58. 接受人电话:
  59. 接受人地址:**省**市张小8
  60. 发送地址:汉东省京州市耐克园区10
  61.  
  62. example.ConcreteKuaidi@5994a1e9
  63. ------------------------------------

分析上述例子。首先看结果,通过打印的对象地址我们可以看出来,虽然邮寄了5个快递,但是由于共享的问题,实际上只是创建了两个对象。运行鞋共享的一个对象。而运动裤共享的一个。由于网点的快递量是很大的。如果这个耐克专卖店就买鞋和裤子,那么理论上鞋共享一个、裤子共享一个对象就够了。这样极大的节省了内存空间的开销。

  虽然另外建了一个不可共享的外部类,但是这个外部类都是很小的。共享对象节省的内存远远大于这个外部类占用内存。

  享元工厂一定要实现要用单例模式来实现。这样更能节省内存,提交效率。

分析享元模式

  享元模式其实是一个很简单的设计模式,他的重点在于分清内部状态、外部状态。内部状态:可共享内容,就是不会变化的内容;外部状态:随着应用场景的变化会变化的状态。内部状态是应用享元模式的基础,一个需要大量产生的对象类,可分离出来的内部状态越多,那么用享元模式的价值就越高;或者共享状态不多,但是可分离成内部状态的属性是一个非常多的内容,占用空间很大,这样使用享元模式的效果也很好。

  享元模式的缺点:由于把原来的大对象换成了多个小颗粒对象,而且加入了享元工厂这个类,所以系统变复杂了。

  线程安全问题,在设置共享对象的时候一定要注意共享的类是否够场景试用,如果并发太高,而共享的对象太少,就会造成线程安全问题。比如都是访问一个类了。就造成了数据错乱了。

复合享元模式

简单来说,就是享元对象在保存后,某种情景下需要的对象是已存在的两种或以上的不同享元对象组合而成的。这样就不要再去保存这个组合成的享元类了。需要的时候用组合生成即可。

享元模式的理解误区

在开始看享元模式的时候,我对内部状态和外部状态的理解不得其所。总觉得应该把不变的设成外部状态,变化的弄成内部。把分离出去的外部状态聚合到咱们的主对象里。这样在使用类的时候就可以复用不变的外部状态了。认真思考一下这样的设计很明显它没有起到享元模式共享类,节省内容的作用。

在反复看了例子以后才明白了享元模式类结构意图。

设计模式之(十二)享元模式(Flyweight)的更多相关文章

  1. 设计模式(十)享元模式Flyweight(结构型)

    设计模式(十)享元模式Flyweight(结构型) 说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释 ...

  2. C#设计模式之十二享元模式(Flyweight)【结构型】

    一.引言   今天我们要讲[结构型]设计模式的第六个模式,该模式是[享元模式],英文名称是:Flyweight Pattern.还是老套路,先从名字上来看看."享元"是不是可以这样 ...

  3. javascript设计模式学习之十二——享元模式

    一.享元模式的定义及使用场景 享元模式是为了解决性能问题而诞生的设计模式,这和大部分设计模式为了提高程序复用性的原因不太一样,如果系统中因为创建了大量类似对象而导致内存占用过高,享元模式就非常有用了. ...

  4. 二十四种设计模式:享元模式(Flyweight Pattern)

    享元模式(Flyweight Pattern) 介绍运用共享技术有效地支持大量细粒度的对象. 示例有一个Message实体类,某些对象对它的操作有Insert()和Get()方法,现在要运用共享技术支 ...

  5. 享元模式 FlyWeight 结构型 设计模式(十五)

    享元模式(FlyWeight)  “享”取“共享”之意,“元”取“单元”之意. 意图 运用共享技术,有效的支持大量细粒度的对象. 意图解析 面向对象的程序设计中,一切皆是对象,这也就意味着系统的运行将 ...

  6. 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern)

    原文:乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) 作者:weba ...

  7. 设计模式-11享元模式(Flyweight Pattern)

    1.模式动机 在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题.创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈. 享元模式就是把相同或相似对象的公共部分提取出 ...

  8. 设计模式系列之享元模式(Flyweight Pattern)——实现对象的复用

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  9. Java设计模式(十一) 享元模式

    原创文章,同步发自作者个人博客 http://www.jasongj.com/design_pattern/flyweight/.转载请注明出处 享元模式介绍 享元模式适用场景 面向对象技术可以很好的 ...

  10. Java设计模式(十二) 策略模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...

随机推荐

  1. 获取APK的appPackage和appActivity

    [法二]AndroidSDK 此方法是采用AndroidSDK\build-tools\23.0.2(这个版本号可能不一定,但是一般每个版本号里面都,任意即可)\aapt.exe aapt dump ...

  2. 201871010110-李华《面向对象程序设计(java)》第十一周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  3. 201871020225-牟星源《面向对象程序设计(java)》第七周学习总结

    201871020225-牟星源<面向对象程序设计(java)>第七周学习总结 博文正文开头: 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu- ...

  4. 前端学习笔记--CSS布局--层定位

    1.层定位概述: z-index:前后叠加顺序 2.position属性: 3.fixed: 2.relative: 移动后: static没有往上移动占据box1的位置. 3.absolute: 移 ...

  5. windows下载的java项目部署到linux的各种解决方案

    1.Java是跨平台的,在linux下有问题,主要一是文件读取权限:二检查下系统环境变量设置可正确!.profile JavaWeb_将Windows平台上开发的JavaWeb项目部署到Linux平台 ...

  6. nginx 常见的问题

    1.server匹配优先级 nginx 读取文件名是按照文件排序优先读取的顺序    对与一样的server 优先使用先读取到的 2.location匹配优先级 =    进行普通字符精确匹配,也就是 ...

  7. 学习:API断点和条件记录断点和内存断点的配合

    前言:感觉可能与之前有点相同,主要是介绍shark恒老师说的一种断点方法,结合了API和条件记录进行下断点 适用条件:当我们利用简单的WINDOWS API函数如MessageBoxW/A 又或者获取 ...

  8. 在树莓派上配置MariaDB

    在树莓派上配置MariaDB 前言 MariaDB是由原本开发MySQL的一些原始开发者领导,他们担心Oracle收购MySQL后会有一些隐患.MariaDB与MySQL保持这高度兼容性,并使用了一个 ...

  9. 生成指定python项目中所有的依赖文件

    一. pipreqs工具 这个工具的好处是可以通过对项目目录的扫描,自动发现使用了那些类库,自动生成依赖清单. 缺点是可能会有些偏差,需要检查并自己调整下. 安装: pip install pipre ...

  10. day 24

    I am a slow walker, but I never walk back. 我走得很慢,但是我从来不会后退.