java设计模式-享元模式(Flyweight Pattern)

172 阅读3分钟

「这是我参与2022首次更文挑战的第34天,活动详情查看:2022首次更文挑战

概念

享元模式(Flyweight Pattern): 运用共享技术有效地支持大量细粒度对象的复用,系统只使用少量对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。

当系统中存在大量相似或相同的对象时,将会导致运行代价过高、性能下降、OOM 等问题,享元模式正为解决之一类问题而诞生

在学习享元模式之前需要先了解一下细粒度和享元对象中的内部状态、外部状态这三个概念:

  • 内部状态:不随环境改变而改变的状态,内部状态可以共享,例如人的性别,不管任何环境下都不会改变
  • 外部状态:随着环境改变而改变的状态,不可以共享的状态,享元对象的外部状态通常由客户端保存,并在享元对象创建后,需要的时候传入享元对象内部,不同的外部状态是相互独立的。例如衣服和鞋子,人在不同的环境下会穿不同的衣服和鞋子,但是衣服和鞋子又是相互独立不受彼此影响的
  • 细粒度:较小的对象,所包含的内部状态较小

类图如下:

image.png

只看概念可能比较抽象,我们来举个例子来说明,假设我们做一个游戏界面,然后游戏界面里面有很多数,我们需要把这些数随机“种”到游戏地图上。
假设我们地图上有两种树,但是总数需要种N棵,按照一般情况就需要创建N个对象,而实际上我们只有两个不同的对象,但是却创建了N个,浪费了许多内存,因此我们就需要用到享元模式,只需要创建两个不同的对象就可以实现我们“种树的”需求了。

相关代码

首先创建树的对象

package com.jony.designpattern.flyweight;

public class Tree {
    private final String name;
    private final String color;

    public String getName() {
        return name;
    }
    public String getColor() {
        return color;
    }
    public Tree(String name, String color) {
        this.name = name;
        this.color = color;
    }
}

再次创建种树节点的对象

package com.jony.designpattern.flyweight;

public class TreeNode {
    private int x;
    private int y;
    private Tree tree;

    public TreeNode(int x, int y, Tree tree) {
        this.x = x;
        this.y = y;
        this.tree = tree;
    }

    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;
    }

    public Tree getTree() {
        return tree;
    }

    public void setTree(Tree tree) {
        this.tree = tree;
    }
}

然后创建获得树对象

package com.jony.designpattern.flyweight;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TreeFactory {
    //第一一个线程安全的map,用于判断数的种类
    private static Map<String,Tree> map=new ConcurrentHashMap<>();
    //获得树对象
    public static Tree getTree(String name,String color){
        if(map.containsKey(name)){
            return map.get(name);
        }
        Tree tree=new Tree(name,color);
        map.put(name,tree);
        return tree;
    }

}

测试代码

package com.jony.designpattern.flyweight;

public class TreeFactoryTest {
    public static void main(String[] args) {
        TreeNode treeNode1=new TreeNode( 3,4, TreeFactory.getTree("杨树","绿色" ));
        TreeNode treeNode2=new TreeNode( 4,5, TreeFactory.getTree("杨树","黄色" ));

        TreeNode treeNode3=new TreeNode( 4,5, TreeFactory.getTree("柳树","黄色" ));
        TreeNode treeNode4=new TreeNode( 4,5, TreeFactory.getTree("柳树","黄色" ));
    }
}

执行结果

image.png

可以看到我们种了4棵树,但是实际只创建了两次对象。

总结

优点

  • 如果系统有大量类似的对象,可以节省大量的内存及CPU资源

缺点

  • 为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂

适用场景

  • 系统中具有大量相同或相似对象
  • 对象大部分状态都可以外部化
  • 享元池耗费一定系统资源,需要多次重复使用享元对象时才值得使用享元模式