1、概述
享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。
享元模式只有一个目的: 将内存消耗最小化。
2、适用场景
仅在程序必须支持大量对象且没有足够的内存容量时使用享元模式。
3、实例
有以下场景:
有100000颗杨树树苗,需要种植。
其中杨树有颜色,名称,树高等属性。
种植杨树。
3.1 不使用享元模式
import java.util.Map;
/**
* 树
* @date: 2021/1/11
* @author weirx
* @version 3.0
*/
public class Tree {
private String name;
private String color;
private Map<String,Object> other;
public Map<String, Object> getOther() {
return other;
}
public void setOther(Map<String, Object> other) {
this.other = other;
}
public Tree(String name, String color, Map<String, Object> other, double high) {
this.name = name;
this.color = color;
this.other = other;
this.high = high;
}
private double high;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getHigh() {
return high;
}
public void setHigh(double high) {
this.high = high;
}
}
测试类:
/**
* 客户端
* @date: 2021/1/4
* @author weirx
* @version 3.0
*/
public class TestDemo {
public static void main(String arg[]) {
System.out.println("开始种树");
List<Tree> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
list.add(new Tree("杨树", "绿色", new HashMap<>(),Math.random() * (10 - 5) + 5));
}
System.out.println("种植完成");
}
}
3.2 使用享元模式
/**
* 树
* @date: 2021/1/11
* @author weirx
* @version 3.0
*/
public class Tree {
private TreeBaseField baseField;
private double high;
public TreeBaseField getBaseField() {
return baseField;
}
public void setBaseField(TreeBaseField baseField) {
this.baseField = baseField;
}
public double getHigh() {
return high;
}
public void setHigh(double high) {
this.high = high;
}
public Tree(TreeBaseField baseField, double high) {
this.baseField = baseField;
this.high = high;
}
}
抽出公共属性
/**
* 树基本属性
* @date: 2021/1/11
* @author weirx
* @version 3.0
*/
public class TreeBaseField {
private String name;
private String color;
private Map<String,Object> map;
public Map<String, Object> getMap() {
return map;
}
public void setMap(Map<String, Object> map) {
this.map = map;
}
public TreeBaseField(String name, String color, Map<String, Object> map) {
this.name = name;
this.color = color;
this.map = map;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
享元工厂:
import org.apache.commons.lang3.ObjectUtils;
import java.util.HashMap;
/**
* 享元工厂
* @date: 2021/1/11
* @author weirx
* @version 3.0
*/
public class FlyweightBeanFactory {
public static HashMap<String, TreeBaseField> map = new HashMap<>();
public static TreeBaseField getBaseField(String name, String color) {
TreeBaseField treeBaseField = map.get(name);
if (ObjectUtils.isEmpty(treeBaseField)) {
map.put(name, new TreeBaseField(name, color,new HashMap<>()));
}
return map.get(name);
}
}
测试类:
import java.util.ArrayList;
import java.util.List;
/**
* 测试类
* @date: 2021/1/11
* @author weirx
* @version 3.0
*/
public class TestDemo {
public static void main(String[] args) {
List<Tree> list = new ArrayList<>();
System.out.println("开始种树");
for (int i = 0; i < 100000; i++) {
list.add(new Tree(FlyweightBeanFactory.getBaseField("杨树", "绿色"),
Math.random() * (10 - 5) + 5));
}
System.out.println("种树完成");
}
}
4、分析
这里我们使用jps命令和jstat命令进行内存分析:
不使用享元的情况下:
E:\workspace\bssp-cloud\bssp-admin-front>jps
31844 TestDemo
E:\workspace\bssp-cloud\bssp-admin-front>jstat -gcutil 31844
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 50.08 0.00 17.54 19.90 0 0.000 0 0.000 0.000
我们发现内存Eden区占用了50.08。
使用享元:
E:\workspace\bssp-cloud\bssp-admin-front>jps
37124 TestDemo
E:\workspace\bssp-cloud\bssp-admin-front>jstat -gcutil 37124
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 36.56 0.00 17.54 19.90 0 0.000 0 0.000 0.000
我们发现内存Eden区占用了36.56。
随着数据量的增加,这两个差距将会更大。
在数据量很小,甚至说主类,比如Tree,其属性很少,都是一些基本类型的,使用享元反而会导致内存占用增加。
5、总结
优点:
如果程序中有很多相似对象, 那么你将可以节省大量内存。
缺点:
1)代码复杂
2)每次使用对象时都增加了判断,增加计算成本。
3)通过实践来决定是否真的需要享元模式,否则会适得其反。
网友评论