Java设计模式学习记录-享元模式
前言
享元模式也是一种结构型模式,这篇是介绍结构型模式的最后一篇了(因为代理模式很早之前就已经写过了)。享元模式采用一个共享来避免大量拥有相同内容对象的开销。这种开销最常见、最直观的就是内存损耗。
享元模式
定义
享元模式是指运用共享技术有效的支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
举例
咖啡问题,在一家咖啡店里有几种口味的咖啡,例如:拿铁、摩卡、卡布奇诺等等。最近这家店在搞促销活动,一中午能卖出几百杯咖啡,那么咖啡的口味就是一种共享的元素。下面用代码来实现一下这个例子。
定义订单接口
/**
* 订单接口
*/
public interface Order { //卖出咖啡
void sell(); }
具体的订单实现
public class CoffeeOrder implements Order { //咖啡口味
public String flavor; public CoffeeOrder(String flavor){
this.flavor = flavor;
} @Override
public void sell() {
System.out.println("卖出了一份"+flavor+"的咖啡。");
}
}
订单工厂类
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.Objects; /**
* 订单工厂类
*/
public class CoffeeOrderFactory { private static Map<String,Order> cof = Maps.newHashMap(); /**
* 获得订单
* @param flavor 口味
* @return
*/
public static Order getOrder(String flavor){ Order order = cof.get(flavor); if(Objects.isNull(order)){
order = new CoffeeOrder(flavor);
cof.put(flavor,order);
}
return order;
} /**
* 获取最终创建的对象个数
* @return
*/
public static int getSize(){ return cof.size();
} }
测试类
import org.assertj.core.util.Lists;
import java.util.List; /**
* 测试买咖啡
*/
public class MyTest { public static void main(String[] args) { buyCoffee("拿铁");
buyCoffee("卡布奇诺");
buyCoffee("摩卡"); buyCoffee("拿铁");
buyCoffee("拿铁");
buyCoffee("拿铁"); buyCoffee("卡布奇诺");
buyCoffee("卡布奇诺");
buyCoffee("卡布奇诺"); buyCoffee("摩卡");
buyCoffee("摩卡");
buyCoffee("摩卡"); //打印出卖出的咖啡
coffeeOrderList.stream().forEach(Order::sell); System.out.println("一共卖出去"+coffeeOrderList.size()+"杯咖啡!");
System.out.println("一共生成了"+CoffeeOrderFactory.getSize()+"个Java对象!"); }
//订单列表
public static List<Order> coffeeOrderList = Lists.newArrayList(); /**
* 买咖啡
* @param flavor 口味
*/
public static void buyCoffee(String flavor){
coffeeOrderList.add(CoffeeOrderFactory.getOrder(flavor));
} }
运行结果
卖出了一份拿铁的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份摩卡的咖啡。
卖出了一份拿铁的咖啡。
卖出了一份拿铁的咖啡。
卖出了一份拿铁的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份摩卡的咖啡。
卖出了一份摩卡的咖啡。
卖出了一份摩卡的咖啡。
一共卖出去12杯咖啡!
一共生成了3个Java对象!
从上面的运行结果可以看出来,虽然卖出去了12杯咖啡,但是最终的口味对象只有3个,因为咖啡口味只有在第一次使用的时候创建,后面就直接使用不会再创建了。
享元模式的分析
下面还是来分析一下享元模式的结构吧,结构图如下:
享元模式涉及到的角色有抽象享元角色、具体享元角色、复合享元角色、享元工厂角色,以及客户端角色。具体说明如下:
- 抽象享元角色(FlyWeight):此角色是所有具体享元类的父级,为这些类规定出需要实现的公共接口或抽象类。上面例子中的Order接口就是代表的这个角色。
- 具体享元角色(ConcreteFlyweight):实现抽象享元角色所规定的接口。有时候具体享元角色有称为单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。上面例子中的CoffeeOrder就是代表的这个角色。
- 复合享元角色(UnsharableFlyweight):复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合对象又称为不可共享对象。这个角色一般很少用。
- 享元工厂角色(FlyweightFactory):负责创建和管理享元角色。此角色必须保证享元对象可以被系统适当地共享。当客户端对象请求一个享元对象时,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。上面例子中的CoffeeOrderFactory就是代表的这个角色。
- 客户端角色(client):此角色调用享元工厂角色来使用具体享元角色。
享元模式总结
单纯享元模式和复合享元模式
标准的享元模式中既包含享元对象又包含非享元对象,但是在实际使用过程中我们会用到具体两种特殊形式的享元模式:单纯享元模式和复合享元模式。
单纯享元模式是指,所有的具体享元对象都是可以共享的,不包括非享元对象。
复合享元模式是指,将一些单纯享元对象使用组合模式加以组合,还可以形成组合享元对象,这样的复合享元对象不能共享,但是它可以分解成单纯享元对象,分解后就可以共享了。
享元模式的优点
1、可以极大的减少内存中对象的数量,使得相同或相似的对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
2、享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
享元模式的缺点
1、享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
2、为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
适用场景
一个系统有大量相同或者相似的对象,造成内存的大量耗费。
对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。
延伸
在JDK中就有使用享元模式的例子,最常见的就是我们使用的String类,大家都知道String类是被final修饰的,所以不会被继承,每次变更都会生成一个新的字符串,这样就有点占内存了。所以如果直接写出了一个字符串,当后面又写出了一个同样的字符串时会自动去堆中(JDK7以上)查看是否已经存在这个字符串了,如果已经存在则直接使用,如果不存在这个时候才在堆中再给开辟一块空间存储字符串。
例如下面的例子:
String testStr = "享元模式";
String testString = "享元模式"; System.out.println(testStr == testString);
System.out.println(testStr.equals(testString));
运行结果:
true
true
想了解更多的设计模式请查看Java设计模式学习记录-GoF设计模式概述。
面试面到怀疑人生,继续加油吧!
Java设计模式学习记录-享元模式的更多相关文章
- Java设计模式之《享元模式》及应用场景
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6542449.html 享元模式:"享"就是分享之意,指一物被众人共享, ...
- 重学 Java 设计模式:实战享元模式「基于Redis秒杀,提供活动与库存信息查询场景」
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 程序员的上下文是什么? 很多时候一大部分编程开发的人员都只是关注于功能的实现,只 ...
- 设计模式学习之享元模式(Flyweight,结构型模式)(20)
转:http://terrylee.cnblogs.com/archive/2006/03/29/361767.html 摘要:面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题.但是 ...
- 设计模式学习心得<享元模式 Flyweight>
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式. 享元模式尝 ...
- 【转】Java设计模式之《享元模式》及应用场景
享元模式:“享”就是分享之意,指一物被众人共享,而这也正是该模式的终旨所在. 享元模式有点类似于单例模式,都是只生成一个对象来被共享使用.这里有个问题,那就是对共享对象的修改,为了避免出现这种情况,我 ...
- java设计模式-----10、享元模式
Flyweight模式也叫享元模式,是构造型模式之一,它通过与其他类似对象共享数据来减小内存占用.它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件:它适合用于只是因重复而导致使 ...
- 《JAVA设计模式》之享元模式(Flyweight)
在阎宏博士的<JAVA与模式>一书中开头是这样描述享元(Flyweight)模式的: Flyweight在拳击比赛中指最轻量级,即“蝇量级”或“雨量级”,这里选择使用“享元模式”的意译,是 ...
- Java设计模式:Flyweight(享元)模式
概念定义 享元(Flyweight)模式运用共享技术高效地支持大量细粒度对象的复用. 当系统中存在大量相似或相同的对象时,有可能会造成内存溢出等问题.享元模式尝试重用现有的同类对象,如果未找到匹配的对 ...
- Java设计模式学习记录-中介者模式
前言 中介者模式听名字就能想到也是一种为了解决耦合度的设计模式,其实中介者模式在结构上与观察者.命令模式十分相像:而应用目的又与结构模式“门面模式”有些相似.但区别于命令模式的是大多数中介者角色对于客 ...
随机推荐
- CentOS6.5在虚拟机中安装
只有一点,先建虚拟机,再选择iso镜像安装,注意,安装路径不能有中文空格之类的. CentOS6.5 64位下载链接 链接:https://pan.baidu.com/s/1d6zp5LtKtkL8I ...
- log4net 写日志
转载地址:https://www.cnblogs.com/vichin/p/6022612.html //基本使用 https://www.cnblogs.com/genesis/p/498562 ...
- 51nod 1344
一个很简单的算法题,求最小的前缀和,就是要注意数据范围要开一个longlong #include<iostream> using namespace std; int main() { i ...
- Shell脚本学习-数组
跟着RUNOOB网站的教程学习的笔记 Shell数组 数组中可以存放多个值,Bash Shell只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与PHP类似). 与大部分编程语言类似,数 ...
- Angularjs自定义指令计算浏览器高度
<!DOCTYPE html> <html ng-app="app"> <head> <title>柳絮飞祭奠</title& ...
- hdu 1325 && poj 1308 Is It A Tree?(并查集)
Description A tree is a well-known data structure that is either empty (null, void, nothing) or is a ...
- sql-向已有数据的表添加约束
语法: alter table 表名 with nocheck add constraint 约束名 约束类型 具体的约束说明 对表中现有的数据不做检查, 只对添加约束后再录入的数据进行检查. 例子: ...
- maven理论基础
Maven介绍 Maven是一个Java项目管理和构建工具 Maven使用pom.xml定义项目内容,并使用预设的目录结构 在Maven中声明一个依赖项可以自动下载并导入classpath Maven ...
- Jenkins高危代码执行漏洞检测/开源漏洞靶场
漏洞细节可以参看安全客的文章:https://bbs.ichunqiu.com/thread-22507-1-1.html Jenkins-CLI 反序列化代码执行(CVE-2017-1000353) ...
- nginx反向代理转发apache配置 之 cookie去哪儿了?
在公司接手了个微信项目,由于微信环境下访问网站需要使用对外开放的域名,所以有相关问题,都是直接运维同事帮忙处理. 原理是这样: 方案一: 1. 将域名解析指向测试服务器的地址: 2. 开放相关端口访问 ...