设计模式之(十二)享元模式(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) ...
随机推荐
- sqlalchemy(2)
orm介绍 orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为 ...
- Appium+python自动化(三)- SDK Manager(超详解)
简介 本来宏哥一开始打算用真机做的,所以在前边搭建环境时候就没有下载SDK,但是由于许多小伙伴通过博客发短消息给宏哥留言说是没有真机,所以顺应民意整理一下模拟器,毕竟“得民心者,得天下”.SDK顾名思 ...
- 04-人脸识别-triplets loss 的解释(转载)
转载至: https://blog.csdn.net/tangwei2014/article/details/46788025 下面是内容: [前言] 最近,learning to rank 的思想逐 ...
- Golang调用Python
https://yq.aliyun.com/articles/117329 Python是时髦的机器学习御用开发语言,Golang是大红大紫的新时代后端开发语言.Python很适合让搞算法的写写模型, ...
- LG1155 「NOIP2008」双栈排序 二分图判定
问题描述 LG1155 题解 \(i,j\)如果不能进入一个栈,要满足存在\(k\),使得\(i<j<k\)且\(a_k<a_i<a_j\) 如果\(i,j\)不能进入一个栈, ...
- 使用async-profiler简单分析zeebe 工作流引擎的性能
刚开始的时候直接使用的系统暴露的prometheus metrics,发现越高的版本反而性能越差,期间使用过了 perf 打算使用perf 生成火焰图的,但是因为符号缺失,只找到了占用较高的任务,详细 ...
- Codeforces Round #530 (Div. 2) F 线段树 + 树形dp(自下往上)
https://codeforces.com/contest/1099/problem/F 题意 一颗n个节点的树上,每个点都有\(x[i]\)个饼干,然后在i节点上吃一个饼干的时间是\(t[i]\) ...
- 开源推荐 - CoDo开源一站式DevOps平台
一群有梦想的年轻人开源了一个云管理平台,他们的口号是:让天下没有996的运维 有幸参与到CoDo项目的开发,这是一个非常棒的一站式开源运维平台,分享给大家 平台介绍 CODO是一款为用户提供企业多混合 ...
- [LeetCode] 269. Alien Dictionary 另类字典
There is a new alien language which uses the latin alphabet. However, the order among letters are un ...
- IDEA中SonarLint的安装与使用
一.SonarLint插件的安装 1.1在线安装 (1)在IDEA菜单栏选择File->Settings,左边栏选择Plugins (2)在线安装选择Browse repositories,搜索 ...