设计模式之享元模式

851 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情

简介

享元模式的英文叫:Flyweight Design Pattern。即“轻量级”设计模式,顾名思义就是代码轻量级化。中文“享元”是指被共享的单元,此处的共享是指共享对象,达到节省资源的目的。

当软件模块创建了大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务调用,直接返回在内存中已有的对象使用,避免重新创建对象,造成资源浪费,也可以提高软件使用效率。

我们常见的String常量池、数据库连接池、线程池等等都是享元模式的应用,享元模式是池技术的重要实现方式。

当我们的系统中需要创建大量的对象,并且对象占用了相对大规模的内存,且这些对象中的部分属性是一样的值,而且后续不会再改变,是固定值,那么我们就可以将这些相同的属性用一个对象来表示,这样就节省了很多内存空间。我们将这些相同的属性共享出来,所有的相同类别的对象共享这个单元,这些属性我们称之为内部状态。

内部状态指对象共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变。

外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

案例

我们还是通过网络上开房间下象棋的经典案例来说明享元设计模式。一个象棋游戏平台肯定有很多的房间,每个房间都有一盘棋,每盘棋上有32颗棋子,就有32个对象,但是每个房间里的【将】的属性有很多都是相同的,比如名字、颜色等,而且它们在各个房间的棋盘中都是不会发生变化的,唯一不同的就是所在的位置不一样。所以相同的属性就可以抽出来作为共享单元。

话不多说,看例子:

享元类,即棋子公共的属性

public class ShareChessAttr {
    private Integer id;
    private String name;
    private String color;
    public ShareChessAttr(Integer id, String name, String color) {
        this.id = id;
        this.name = name;
        this.color = color;
    }
}

定义一个工厂去获取对应棋子的享元

public class ShareChessAttrFactory {
    public static final HashMap<Integer, ShareChessAttr> shareChessAttrMap = new HashMap();
    static {
        shareChessAttrMap.put(1, new ShareChessAttr(1, "将", "红"));
        shareChessAttrMap.put(2, new ShareChessAttr(2, "帅", "黑"));
        shareChessAttrMap.put(3, new ShareChessAttr(3, "车", "红"));
        shareChessAttrMap.put(4, new ShareChessAttr(4, "车", "黑"));
    }
    public static ShareChessAttr getShareChessAttr(Integer i) {
        return shareChessAttrMap.get(i);
    }
}

定义棋子

public class Chess {
    private ShareChessAttr shareChessAttr;
    private Integer positionX;
    private Integer positionY;
    public Chess(ShareChessAttr shareChessAttr, Integer positionX, Integer positionY) {
        this.shareChessAttr = shareChessAttr;
        this.positionX = positionX;
        this.positionY = positionY;
    }
}

定义棋盘

public class ChessBoard {
    private HashMap<Integer, Chess> chessOnBoard = new HashMap<Integer, Chess>();
    public ChessBoard() {
        chessOnBoard.put(1, new Chess(ShareChessAttrFactory.getShareChessAttr(1), 5, 0));
        chessOnBoard.put(2, new Chess(ShareChessAttrFactory.getShareChessAttr(2), 5, 0));
    }
}

如此每个棋盘中的棋子的id,名字和颜色都指向了同一个shareChessAttr。实现享元。

总结

享元模式的思想与单例比较类似,但是单例模式强调的是全局唯一,而享元模式则强调的是内存共享。