20、FlyWeight 享元模式
池化的思想
1、Flyweight享元模式
运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
面向对象很好地解决了“抽象”的问题,但是必不可免地要付出一定的代价。对于通常情况来说,面向对象的成本大都可以忽略不计。但是某些情况,面向对象所带来的成本必须谨慎处理。
意图:运用共享技术有效地支持大量细粒度的对象。
主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
何时使用: 1、系统中有大量对象。2、这些对象消耗大量内存。3、这些对象的状态大部分可以外部化。4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。5、系统不依赖于这些对象身份,这些对象是不可分辨的。
使用场景: 1、系统有大量相似对象。2、需要缓冲池的场景。
2、示例代码
使用享元模式来设计围棋软件中的棋子
IgoChessman充当抽象享元类,BlackIgoChessman和WhiteIgoChessman充当具体享元类,IgoChessmanFactory充当享元工厂类。完整代码如下所示:
import java.util.*;
//围棋棋子类:抽象享元类
abstract class IgoChessman {
public abstract String getColor();
public void display() {
System.out.println("棋子颜色:" + this.getColor());
}
}
//黑色棋子类:具体享元类
class BlackIgoChessman extends IgoChessman {
public String getColor() {
return "黑色";
}
}
//白色棋子类:具体享元类
class WhiteIgoChessman extends IgoChessman {
public String getColor() {
return "白色";
}
}
//围棋棋子工厂类:享元工厂类,使用单例模式进行设计
class IgoChessmanFactory {
private static IgoChessmanFactory instance = new IgoChessmanFactory();
private static Hashtable ht; //使用Hashtable来存储享元对象,充当享元池 private IgoChessmanFactory() {
ht = new Hashtable();
IgoChessman black,white;
black = new BlackIgoChessman();
ht.put("b",black);
white = new WhiteIgoChessman();
ht.put("w",white);
} //返回享元工厂类的唯一实例
public static IgoChessmanFactory getInstance() {
return instance;
} //通过key来获取存储在Hashtable中的享元对象
public static IgoChessman getIgoChessman(String color) {
return (IgoChessman)ht.get(color);
}
}
编写如下客户端测试代码:
class Client {
public static void main(String args[]) {
IgoChessman black1,black2,black3,white1,white2;
IgoChessmanFactory factory;
//获取享元工厂对象
factory = IgoChessmanFactory.getInstance();
//通过享元工厂获取三颗黑子
black1 = factory.getIgoChessman("b");
black2 = factory.getIgoChessman("b");
black3 = factory.getIgoChessman("b");
System.out.println("判断两颗黑子是否相同:" + (black1==black2));
//通过享元工厂获取两颗白子
white1 = factory.getIgoChessman("w");
white2 = factory.getIgoChessman("w");
System.out.println("判断两颗白子是否相同:" + (white1==white2));
//显示棋子
black1.display();
black2.display();
black3.display();
white1.display();
white2.display();
}
}
编译并运行程序,输出结果如下:
判断两颗黑子是否相同:true
判断两颗白子是否相同:true
棋子颜色:黑色
棋子颜色:黑色
棋子颜色:黑色
棋子颜色:白色
棋子颜色:白色
从输出结果可以看出,虽然我们获取了三个黑子对象和两个白子对象,但是它们的内存地址相同,也就是说,它们实际上是同一个对象。在实现享元工厂类时我们使用了单例模式和简单工厂模式,确保了享元工厂对象的唯一性,并提供工厂方法来向客户端返回享元对象。
引入外部状态
发现虽然黑色棋子和白色棋子可以共享,但是它们将显示在棋盘的不同位置,如何让相同的黑子或者白子能够多次重复显示且位于一个棋盘的不同地方?解决方法就是将棋子的位置定义为棋子的一个外部状态,在需要时再进行设置。
除了增加一个坐标类Coordinates以外,抽象享元类IgoChessman中的display()方法也将对应增加一个Coordinates类型的参数,用于在显示棋子时指定其坐标,Coordinates类和修改之后的IgoChessman类的代码如下所示:
//坐标类:外部状态类
class Coordinates {
private int x;
private int y;
public Coordinates(int x,int y) {
this.x = x;
this.y = y;
}
public int getX() {
return this.x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return this.y;
}
public void setY(int y) {
this.y = y;
}
}
//围棋棋子类:抽象享元类
abstract class IgoChessman {
public abstract String getColor();
public void display(Coordinates coord){
System.out.println("棋子颜色:" + this.getColor() + ",棋子位置:" + coord.getX() + "," + coord.getY() );
}
}
客户端测试代码修改如下:
class Client {
public static void main(String args[]) {
IgoChessman black1,black2,black3,white1,white2;
IgoChessmanFactory factory;
//获取享元工厂对象
factory = IgoChessmanFactory.getInstance();
//通过享元工厂获取三颗黑子
black1 = factory.getIgoChessman("b");
black2 = factory.getIgoChessman("b");
black3 = factory.getIgoChessman("b");
System.out.println("判断两颗黑子是否相同:" + (black1==black2));
//通过享元工厂获取两颗白子
white1 = factory.getIgoChessman("w");
white2 = factory.getIgoChessman("w");
System.out.println("判断两颗白子是否相同:" + (white1==white2));
//显示棋子,同时设置棋子的坐标位置
black1.display(new Coordinates(1,2));
black2.display(new Coordinates(3,4));
black3.display(new Coordinates(1,3));
white1.display(new Coordinates(2,5));
white2.display(new Coordinates(2,4));
}
}
编译并运行程序,输出结果如下:
判断两颗黑子是否相同:true
判断两颗白子是否相同:true
棋子颜色:黑色,棋子位置:1,2
棋子颜色:黑色,棋子位置:3,4
棋子颜色:黑色,棋子位置:1,3
棋子颜色:白色,棋子位置:2,5
棋子颜色:白色,棋子位置:2,4
从输出结果可以看到,在每次调用display()方法时,都设置了不同的外部状态——坐标值,因此相同的棋子对象虽然具有相同的颜色,但是它们的坐标值不同,将显示在棋盘的不同位置。
3、flyweight类图
登场角色
◆Flyweight (轻量级)按照通常方式编写程序会导致程序变重,所以如臬能够共享实例会比较好,而Flyweight角色表示的就是那些实例会被共享的类。在示例程序中,由BigChar类扮演此角色。
◆FlyweightFactory (轻量级工厂)FlyweightFactory角色是生成Flyweight角色的工厂。在工厂中生成Flyweight角色可以实现共享实例。在示例程序中,由BigCharFactory类扮演此角色。
◆Client (请求者)Client角色使用FlyweightFactory角色来生成Flyweight角色。在示例程序中,由BigString类扮演此角色。
4、小结
当系统中存在大量相同或者相似的对象时,享元模式是一种较好的解决方案,它通过共享技术实现相同或相似的细粒度对象的复用,从而节约了内存空间,提高了系统性能。相比其他结构型设计模式,享元模式的使用频率并不算太高,但是作为一种以“节约内存,提高性能”为出发点的设计模式,它在软件开发中还是得到了一定程度的应用。
适用场景
(1) 一个系统有大量相同或者相似的对象,造成内存的大量耗费。
(2) 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
(3) 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。
在以下情况下可以考虑使用享元模式:
(1) 一个系统有大量相同或者相似的对象,造成内存的大量耗费。
(2) 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
(3) 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。
公众号:发哥讲
这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。
如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~
● 扫码关注我们
据说看到好文章不推荐的人,服务器容易宕机!
本文版权归发哥讲和博客园共有,原创文章,未经允许不得转载,否则保留追究法律责任的权利。
20、FlyWeight 享元模式的更多相关文章
- 设计模式之flyweight享元模式
运用共享技术支持大量细粒度对象的使用 Flyweight模式(享元) Java深入到一定程度,就不可避免的碰到设计模式这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解.设计模 ...
- 设计模式11: Flyweight 享元模式(结构型模式)
Flyweight 享元模式(结构型模式) 面向对象的代价 面向对象很好的解决了系统抽象性的问题,同时在大多数情况下也不会损及系统的性能.但是,在某些特殊应用中,由于对象的数量太大,采用面向对象会给系 ...
- C++设计模式-Flyweight享元模式
Flyweight享元模式 作用:运用共享技术有效地支持大量细粒度的对象. 内部状态intrinsic和外部状态extrinsic: 1)Flyweight模式中,最重要的是将对象分解成intrins ...
- Flyweight享元模式(结构型模式)
1.面向对象的缺点 虽然OOP能很好的解决系统抽象的问题,并且在大多数的情况下,也不会损失系统的性能.但是在某些特殊的业务下,由于对象的数量太多,采用面向对象会给系统带来难以承受的内存开销.示例代码如 ...
- Flyweight(享元模式)
import java.util.Hashtable; /** * 享元模式 * @author TMAC-J * 享元模式一般和工厂模式一起使用,但此处为了更好说明,只用享元模式 * 定义:享元模式 ...
- 设计模式(十二): Flyweight享元模式 -- 结构型模式
说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释放.我们只是为了学习而简单做了介绍. 1. 概述 面 ...
- 二十、Flyweight 享元模式
原理: 代码清单: BigChar public class BigChar { //字符名称 private char charname; //大型字符 # . \n 组成 private Stri ...
- 设计模式(11)--Flyweight(享元模式)--结构型
作者QQ:1095737364 QQ群:123300273 欢迎加入! 1.模式定义: 享元模式是对象的结构模式.享元模式以共享的方式高效地支持大量的细粒度对象. 2.模式特点: 享元模 ...
- Flyweight 享元模式 MD
享元模式 简介 在JAVA语言中,String类型就是使用了享元模式,JAVA中的字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝,避免了在创建N多相同对象时所产生的不 ...
随机推荐
- 数据可视化之PowerQuery篇(十二)客户购买频次分布
https://zhuanlan.zhihu.com/p/100070260 商业数据分析通常都可以简化为对数据进行筛选.分组.汇总的过程,本文通过一个实例来看看PowerBI是如何快速完成整个过程的 ...
- git本地创建分支,并提交到github上去
很多时候,我们再开发的时候需要分支. 那么怎么在本地创建分支,并提交到github或者是远程仓库中呢? 其实很简单: 第一步: git checkout -b dev 创建新的分支 第二步: ...
- 第二章: IPC机制
这章关于进程的概念,没有深入太多,只做了解跟学习 IPC: Inter-Process Communication,进程间通信或者跨进程通信,两个进程之间进行数据交换的过程 2.1介绍 线程:CPU调 ...
- OSCP Learning Notes - Post Exploitation(4)
Pivoting 1. Edit the virtual network settings of the Vmware. 2. Set the Network Adapter(s) of Kali L ...
- 用前端姿势玩docker【四】基于docker快速构建webpack的开发与生产环境
目录 用前端姿势玩docker[一]Docker通俗理解常用功能汇总与操作埋坑 用前端姿势玩docker[二]dockerfile定制镜像初体验 用前端姿势玩docker[三]基于nvm的前端环境构建 ...
- python 复制以及更改列表操作
题目:设置一个老用户列表和一个新用户列表,检查老用户列表中是否与新注册的用户名字有重复(不区分大小写),老用户列表不能被破坏 usernames = ['admin','Tom','john','ja ...
- C++ 优先队列priority_queue用法
头文件:#include<queue> 操作: top 访问队头 empty 队列是否为空 size 返回队列元素个数 push 插入元素到队尾 pop 弹出队头 swap 交换内容 定义 ...
- C#串模板
c# 6.0 的语言特性,功能类似string.formate,更方便的地方在于不要像format一样使用索引,可以直接使用变量. 使用方法如下: string name = "zhangs ...
- ThinkPHP6 核心分析之Http 类跟Request类的实例化
以下源码分析,我们可以从 App,Http 类的实例化过程,了解类是如何实现自动实例化的,依赖注入是怎么实现的. 从入口文件出发 当访问一个 ThinkPHP 搭建的站点,框架最先是从入口文件开始的, ...
- ELK5.6.4+Redis+Filebeat+Nginx(CentOS7.4)
下载地址: Elasticsearhc: https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.4.tar.gz ...