Java 核心技术Java 进阶编程语言爱好者

设计模式(十二)享元模式

2021-01-11  本文已影响0人  我犟不过你

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)通过实践来决定是否真的需要享元模式,否则会适得其反。

上一篇 下一篇

猜你喜欢

热点阅读