大家好✋,我是知识汲取者😄,今天给大家带来一篇有关享元模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的稳健性、可移植性、可维护性,同时还能让你的代码更加精炼,具备艺术美感。
有时候我们在系统中由于对象创建的数量过多,会造成内存溢出,比如我们设计一个下围棋的程序,黑子有181颗、白子有180颗,总共有361颗,如果我们一股脑地不加以注意,每落一颗子就创建一个对象的化,那问题就大量,得创建大量对象。而享元模式的出现就能很好地解决这一弊端,很大程度地节约了内存,同时也能够提高程序的性能,享元模式有类似于线程池,它可以提供一个享元池,享元池中的对象可以被共享,能够被系统复用,从而大大降低对象的创建数量……话不多说,且看下文
推荐阅读:
什么是享元模式?
享元模式(Flyweight Pattern)是一种结构型模式,它利用共享技术实现对象的复用,进而减少内存占用和提高程序性能
享元模式的作用:减少对象的创建,减少内存占用和提高程序性能
享元模式的优缺点
优点:
……
缺点:
……
享元模式的引用场景:
系统需要创建大量粒度较小且相似的对象,可以使用享元模式
系统需要一个缓冲池,可以使用享元模式进行实现
……
享元模式的角色划分:
相关概念:
示例:
围棋分为黑子和白子两种,所有的棋子非黑即白,所以棋子的颜色是可以被共享的,这是围棋的内部状态;所有的围棋在落子后都有一个独一无二的坐标,所以围棋的落子坐标是不可以被共享的,这是围棋的外部状态。使用享元模式实现围棋对象的创建
Step1:创建抽象享元类
package com.hhxy.chessman;
/**
* @author ghp
* @date 2022/10/19
* @title 棋子的抽象类
* @description
*/
public abstract class Chessman {
protected String color;//围棋的颜色属性,它是棋子的内部状态,享元池中所有的棋子对象共享
protected Coordinates coordinates;//围棋的坐标属性,它是棋子外部状态,享元池中棋子对象无法共享
/**
* 获取棋子的颜色
*/
public abstract String getColor();
/**
* 展示棋子的颜色
*/
public void showColor(){
System.out.print("棋子颜色为: " + this.getColor() + "; ");
}
/**
* 展示棋子的坐标
*/
public void showCoordinates(Coordinates coordinates){
this.coordinates = coordinates;
System.out.print("棋子的坐标为: " + "(" + this.coordinates.getX() + "," + this.coordinates.getY() + ")");
}
}
Step2:创建具体享元类
1)黑子:
package com.hhxy.chessman;
/**
* @author ghp
* @date 2022/10/19
* @title
* @description
*/
public class BlackChessman extends Chessman{
public BlackChessman() {
this.color = "黑色";
}
/**
* 获取棋子的颜色
*/
@Override
public String getColor() {
return this.color;
}
}
2)白子:
package com.hhxy.chessman;
/**
* @author ghp
* @date 2022/10/19
* @title
* @description
*/
public class WhiteChessman extends Chessman{
public WhiteChessman() {
this.color = "白色";
}
/**
* 获取棋子的颜色
*/
@Override
public String getColor() {
return this.color;
}
}
Step3:创建非共享享元类
package com.hhxy.chessman;
/**
* @author ghp
* @date 2022/10/19
* @title 坐标类
* @description 用于标识围棋的外部状态
*/
public class Coordinates {
private int x;//围棋棋子的横坐标
private int y;//围棋棋子的纵坐标
public Coordinates(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
Step4:创建享元工厂
package com.hhxy.factory;
import com.hhxy.chessman.BlackChessman;
import com.hhxy.chessman.Chessman;
import com.hhxy.chessman.WhiteChessman;
import java.util.HashMap;
import java.util.Map;
/**
* @author ghp
* @date 2022/10/19
* @title 棋子工厂类
* @description 内部含有一个享元池,是享元模式的核心,用于批量创建同类型且具有共同属性的对象
*/
public class ChessmanFactory {
private static ChessmanFactory instance;
private static Map<String,Object> map;//享元池
private ChessmanFactory(){
//提前将棋子对象存入享元池中
map = new HashMap<>();
WhiteChessman whiteChessman = new WhiteChessman();
BlackChessman blackChessman = new BlackChessman();
map.put("whiteChessman",whiteChessman);
map.put("blackChessman",blackChessman);
}
//使用单例模式创建享元工厂对象,提高效率的同时也能节约内存
static{
instance = new ChessmanFactory();
}
/**
* 获取单例的工厂对象
*/
public static ChessmanFactory getInstance() {
return instance;
}
public Chessman getChessman(String chessmanName){
return (Chessman) map.get(chessmanName);
}
}
Step5:编写测试类
package com.hhxy.test;
import com.hhxy.chessman.Chessman;
import com.hhxy.chessman.Coordinates;
import com.hhxy.factory.ChessmanFactory;
/**
* @author ghp
* @date 2022/10/19
* @title 测试类
* @description 用于测试享元模式
*/
public class Test {
public static void main(String[] args) {
Chessman[] chessmen = new Chessman[4];
//获取棋子的工厂对象
ChessmanFactory chessmanFactory = ChessmanFactory.getInstance();
//从享元池中获取棋子对象
chessmen[0] = chessmanFactory.getChessman("whiteChessman");
chessmen[1] = chessmanFactory.getChessman("whiteChessman");
chessmen[2] = chessmanFactory.getChessman("blackChessman");
chessmen[3] = chessmanFactory.getChessman("blackChessman");
//判断创建的对象是否是同一个,输出ture则证明是同一个对象
System.out.println("判断两个黑棋是否相同:" + (chessmen[0].hashCode() == chessmen[1].hashCode()));
System.out.println("判断两个白棋是否相同:" + (chessmen[2].hashCode() == chessmen[3].hashCode()));
//展示每个棋子的颜色(棋子的内部状态)和坐标(棋子的外部状态)
System.out.print("chessmen[0] ==> ");
chessmen[0].showColor();
chessmen[0].showCoordinates(new Coordinates(1,2));
System.out.println();
System.out.print("chessmen[1] ==> ");
chessmen[1].showColor();
chessmen[1].showCoordinates(new Coordinates(3,4));
System.out.println();
System.out.print("chessmen[2] ==> ");
chessmen[2].showColor();
chessmen[2].showCoordinates(new Coordinates(5,6));
System.out.println();
System.out.print("chessmen[3] ==> ");
chessmen[3].showColor();
chessmen[3].showCoordinates(new Coordinates(7,8));
System.out.println();
}
}
测试结果:
享元模式通过享元池能够进行对象的复用,很大程度的节约了系统的内存,但是享元对象必须是较小粒度,否则会占用较大内存,同时需要注意外部状态和内部状态的划分。关于Flyweight模式,一言以蔽之就是“通过尽量共享实例来避免new出实例”
相关设计模式
Singleton 模式:在FlyweightFactory角色中有时会使用Singleton模式。此外,如果使用了Singleton模式,由于只会生成一个Singleton角色,因此所有使用该实例的地方都共享同一个实例。在Singleton角色的实例中只持有intrinsic信息
Facatory 模式:享元模式一般都需要搭配工厂模式一起实现,才能最大程度减少内存的消耗(它们是天造地设的一对)
Composite 模式:有时可以使用Flyweight模式共享Composite模式中的Leaf角色
Proxy 模式:如果生成实例的处理需要花费较长时间,那么使用Flyweight模式可以提高程序的处理速度而Proxy模式则是通过设置代理提高程序的处理速度
自此,文章就结束了,如果觉得本文对你有一丢丢帮助的话😄,欢迎点赞👍+评论✍,您的支持将是我写出更加优秀文章的动力O(∩_∩)O
上一篇:每日一个设计模式之【外观模式】
下一篇:每人一个设计模式之【代理模式】
参考文章:
- 图解设计模式
- 菜鸟教程
在次致谢