设计模式之(十二)享元模式(Flyweight)
享元模式思想
就以小时候导锅来说吧(导锅是我家乡的方言,就是用细沙把一个模型锅的形状拓下来,然后把铝水倒进模型中,就导好一个锅了。小时候很喜欢看的,可惜现在看不到了。上个图片回忆下)了解了这个过程后就知道,沙子、模型锅、都是可以一直使用的。而用做化铝水的物品是需要不同铝制品东西化生成。做这个生意的人不可能用一次沙子和模型锅就因为用过不用了。
在代码的世界里,那些不变的类属性就像这些沙子和模型锅可以共享使用,而变化的类属性就像为导锅化铝水的铝制品一样是不同变化的。但实际上很多程序员就没有共享那些不变属性,而是生成一个类,就新建一个这样的可共享属性。就如同导锅用了一次的锅和沙子就弃之不用了,下次再用新的一样,多么可笑的事情啊,而享元模式就是为了阻止我们干这样可笑的蠢事而生的。
来看下共享模式的定义:运用共享技术有效地支持大量细粒度的对象,达到节省内存的目的。咋一看不太明白这句话的意思,其实就是本文开篇描述的那个意思。
享元模式结构
模式参与成员:
Flyweight:享元抽象接口
FlyweightFactory:管理共享享元对象的缓存池。
ConcreteFlyweight:可共享的类,里面存放着都是不会随着场景变化而变化的状态,同时介绍不可共享的状态的传入。
Client:模拟试用享元对象的地方(对享元模式来说就是客户端)
例子
为了更好的来理解享元模式,我们来举个例子。就以网店邮寄快递这个场景为例子展示此模式。
一个快递对象都有哪些属性呢,来列举下。快递物品名称,价格,邮寄人,邮寄人电话,邮寄人地址,接收人姓名,接收人电话,接收人地址。既然要用享元模式了就要根据此模式的特征来分析一下,可共享(不变)状态和不可共享(随着不同包裹变化的)状态。
不变的状态有:邮寄人,邮寄人电话,邮寄人地址。我们把相同的物品共享一个对象的话,还有物品名称、价格。
不可共享的状态:接收人姓名,接收人电话,接收人地址。
分好这变化状态和不变状态以后,接下来就构建代码了。
享元抽象接口
//抽象接口,相当于 Flyweight
public interface KuaiDi {
// jsr 接受不可共享部分
public void sendkd(Jiesr jsr);
}
实现接口的可共享类
public class ConcreteKuaidi implements KuaiDi { private String kdName;
private String fsrName;
private String fsrPhone;
private String fsrAddress; public ConcreteKuaidi(String name,String fsrName,String fsrdh,String fsradrress){
this.kdName = name;
this.fsrName = fsrName;
this.fsrPhone = fsrdh;
this.fsrAddress = fsradrress;
} /* getter 和 setter 方法省略了 */ @Override
public void sendkd(Jiesr jsr) {
// TODO Auto-generated method stub
String msg = "邮寄"+ kdName +"给"+jsr.getJsrName()+"\n";
msg = msg + "接受人电话:"+jsr.getJsrPhone()+"\n";
msg = msg + "接受人地址:"+jsr.getAddress()+"\n";
msg = msg + "发送地址:"+fsrAddress+"\n";
System.out.println(msg);
// 打印类对象地址,更好的看出来类共享
System.out.println(this.toString());
System.out.println("------------------------------------");
} }
不可共享部分的类, 快递接收人
public class Jiesr {
private String jsrName;
private String jsrPhone;
private String Address; public String getJsrName() {
return jsrName;
} public String getJsrPhone() {
return jsrPhone;
} public String getAddress() {
return Address;
} public Jiesr(String jsrName,String jsrPhone,String Address){
this.jsrName = jsrName;
this.jsrPhone = jsrPhone;
this.Address = Address;
}
}
享元工厂,实现成为单例模式,是共享以前生成的对象,还是需要新建一个对象,由这儿决定。
public class KudiFactory {
private static KudiFactory kudiFactory = new KudiFactory(); private KudiFactory(){} public static KudiFactory getInstance(){
return kudiFactory;
}
//用于存储 对象
private Map<String,KuaiDi> map = new HashMap<String,KuaiDi>(); // 类似工厂,存有对象池,在这儿生成对象
public KuaiDi getKuaidi(String name,String fsrName,String fsrdh,String fsradrress){
KuaiDi kd = map.get(name);
if(kd == null){
kd = new ConcreteKuaidi(name,fsrName,fsrdh,fsradrress);
map.put(name,kd);
}
return kd;
}
}
调用
public class Client {
public static void main(String[] args) { KudiFactory kk = KudiFactory.getInstance();
KuaiDi kuaidi1 = kk.getKuaidi("耐克运动鞋", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
Jiesr jsr1 = new Jiesr("张小四", "", "**省**市张小四村");
kuaidi1.sendkd(jsr1); KuaiDi kuaidi2 = kk.getKuaidi("耐克运动库", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
Jiesr jsr2 = new Jiesr("王小五", "", "**省**市张小5村");
kuaidi2.sendkd(jsr2); KuaiDi kuaidi3 = kk.getKuaidi("耐克运动鞋", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
Jiesr jsr3 = new Jiesr("李小六", "", "**省**市张小6村");
kuaidi3.sendkd(jsr3); KuaiDi kuaidi4 = kk.getKuaidi("耐克运动库", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
Jiesr jsr4 = new Jiesr("钱小七", "", "**省**市张小7村");
kuaidi4.sendkd(jsr4); KuaiDi kuaidi5 = kk.getKuaidi("耐克运动鞋", "王小二耐克专营店", "", "汉东省京州市耐克园区10号");
Jiesr jsr5 = new Jiesr("孙小八", "", "**省**市张小8村");
kuaidi5.sendkd(jsr5); }
} /*-------------------------------------执行结果-------------------------------*/
邮寄耐克运动鞋给张小四
接受人电话:
接受人地址:**省**市张小四村
发送地址:汉东省京州市耐克园区10号 example.ConcreteKuaidi@5994a1e9
------------------------------------
邮寄耐克运动库给王小五
接受人电话:
接受人地址:**省**市张小5村
发送地址:汉东省京州市耐克园区10号 example.ConcreteKuaidi@2d11f5f1
------------------------------------
邮寄耐克运动鞋给李小六
接受人电话:
接受人地址:**省**市张小6村
发送地址:汉东省京州市耐克园区10号 example.ConcreteKuaidi@5994a1e9
------------------------------------
邮寄耐克运动库给钱小七
接受人电话:
接受人地址:**省**市张小7村
发送地址:汉东省京州市耐克园区10号 example.ConcreteKuaidi@2d11f5f1
------------------------------------
邮寄耐克运动鞋给孙小八
接受人电话:
接受人地址:**省**市张小8村
发送地址:汉东省京州市耐克园区10号 example.ConcreteKuaidi@5994a1e9
------------------------------------
分析上述例子。首先看结果,通过打印的对象地址我们可以看出来,虽然邮寄了5个快递,但是由于共享的问题,实际上只是创建了两个对象。运行鞋共享的一个对象。而运动裤共享的一个。由于网点的快递量是很大的。如果这个耐克专卖店就买鞋和裤子,那么理论上鞋共享一个、裤子共享一个对象就够了。这样极大的节省了内存空间的开销。
虽然另外建了一个不可共享的外部类,但是这个外部类都是很小的。共享对象节省的内存远远大于这个外部类占用内存。
享元工厂一定要实现要用单例模式来实现。这样更能节省内存,提交效率。
分析享元模式
享元模式其实是一个很简单的设计模式,他的重点在于分清内部状态、外部状态。内部状态:可共享内容,就是不会变化的内容;外部状态:随着应用场景的变化会变化的状态。内部状态是应用享元模式的基础,一个需要大量产生的对象类,可分离出来的内部状态越多,那么用享元模式的价值就越高;或者共享状态不多,但是可分离成内部状态的属性是一个非常多的内容,占用空间很大,这样使用享元模式的效果也很好。
享元模式的缺点:由于把原来的大对象换成了多个小颗粒对象,而且加入了享元工厂这个类,所以系统变复杂了。
线程安全问题,在设置共享对象的时候一定要注意共享的类是否够场景试用,如果并发太高,而共享的对象太少,就会造成线程安全问题。比如都是访问一个类了。就造成了数据错乱了。
复合享元模式
简单来说,就是享元对象在保存后,某种情景下需要的对象是已存在的两种或以上的不同享元对象组合而成的。这样就不要再去保存这个组合成的享元类了。需要的时候用组合生成即可。
享元模式的理解误区
在开始看享元模式的时候,我对内部状态和外部状态的理解不得其所。总觉得应该把不变的设成外部状态,变化的弄成内部。把分离出去的外部状态聚合到咱们的主对象里。这样在使用类的时候就可以复用不变的外部状态了。认真思考一下这样的设计很明显它没有起到享元模式共享类,节省内容的作用。
在反复看了例子以后才明白了享元模式类结构意图。
设计模式之(十二)享元模式(Flyweight)的更多相关文章
- 设计模式(十)享元模式Flyweight(结构型)
设计模式(十)享元模式Flyweight(结构型) 说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释 ...
- C#设计模式之十二享元模式(Flyweight)【结构型】
一.引言 今天我们要讲[结构型]设计模式的第六个模式,该模式是[享元模式],英文名称是:Flyweight Pattern.还是老套路,先从名字上来看看."享元"是不是可以这样 ...
- javascript设计模式学习之十二——享元模式
一.享元模式的定义及使用场景 享元模式是为了解决性能问题而诞生的设计模式,这和大部分设计模式为了提高程序复用性的原因不太一样,如果系统中因为创建了大量类似对象而导致内存占用过高,享元模式就非常有用了. ...
- 二十四种设计模式:享元模式(Flyweight Pattern)
享元模式(Flyweight Pattern) 介绍运用共享技术有效地支持大量细粒度的对象. 示例有一个Message实体类,某些对象对它的操作有Insert()和Get()方法,现在要运用共享技术支 ...
- 享元模式 FlyWeight 结构型 设计模式(十五)
享元模式(FlyWeight) “享”取“共享”之意,“元”取“单元”之意. 意图 运用共享技术,有效的支持大量细粒度的对象. 意图解析 面向对象的程序设计中,一切皆是对象,这也就意味着系统的运行将 ...
- 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern)
原文:乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) 作者:weba ...
- 设计模式-11享元模式(Flyweight Pattern)
1.模式动机 在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题.创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈. 享元模式就是把相同或相似对象的公共部分提取出 ...
- 设计模式系列之享元模式(Flyweight Pattern)——实现对象的复用
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- Java设计模式(十一) 享元模式
原创文章,同步发自作者个人博客 http://www.jasongj.com/design_pattern/flyweight/.转载请注明出处 享元模式介绍 享元模式适用场景 面向对象技术可以很好的 ...
- Java设计模式(十二) 策略模式
原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...
随机推荐
- mysql的floor()报错注入方法详细分析
刚开始学习sql注入,遇见了 select count(*) from table group by floor(rand(0)*2); 这么条语句.在此做个总结. (更好的阅读体验可访问 这里 ) ...
- 基于VLC库C#开发可播放摄像头及任意格式视频的播放器
前言 本文主要讲述,在WPF中,借助Vlc.DotNet调用VLC类库,实现视频播功能,下面我们先来做开发前的准备工作. 准备工作 首先,我们创建一个项目WpfVLC,然后,进入Neget搜索Vlc. ...
- E07【餐厅】What would you recommend?
核心句型 What would you recommend? 你有什么推荐吗? 场景对话: A:What do you want to eat? 你想吃点什么? B:I don't know. ...
- IDEA结合Maven的profile构建不同开发环境(SpringBoot)
一.概述 在开发过程中,我们的项目会存在不同的开发环境,比如开发环境.生产环境.测试环境,而我们的项目在不同的环境中有些配置也是不一样的,比如数据源配置.日志文件配置等,假如我们每次将软件部署到不同的 ...
- 20190922 「HZOJ NOIP2019 Round #7」20190922模拟
综述 这次是USACO2019JAN Gold的题目. \(\mathrm{Cow Poetry}\) 题解 因为每句诗的长度一定是\(k\),所以自然而然想到背包. 设\(opt[i][j]\)代表 ...
- window.open()与window.showModuleDialog()
一.window.showModalDialog() 模态对话框. (只支持IE浏览器)window.showModelessDialog() 非模态对话框. 基本语法:vReturnVa ...
- 2019 SDN上机第3次作业
1. 利用Mininet仿真平台构建如下图所示的网络拓扑,配置主机h1和h2的IP地址(h1:10.0.0.1,h2:10.0.0.2),测试两台主机之间的网络连通性 创建拓扑 配置主机h1和h2的I ...
- [LeetCode] 99. Recover Binary Search Tree 复原二叉搜索树
Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...
- .NET Core:跨域
在Startup中的ConfigureServices方法中配置:services.AddCors(options => options.AddPolicy("any", b ...
- 微信企业号消息接口PHP SDK
微信企业号消息接口PHP SDK及Demo <?php /* 方倍工作室 http://www.fangbei.org/ CopyRight 2015 All Rights Reserved * ...