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

作者: 我犟不过你 | 来源:发表于2021-01-11 16:05 被阅读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)通过实践来决定是否真的需要享元模式,否则会适得其反。

    相关文章

      网友评论

        本文标题:设计模式(十二)享元模式

        本文链接:https://www.haomeiwen.com/subject/gbgloktx.html