顾名思义就是共享的单元,享元模式的意图就是复用对象,从而节省对应的内存

对于一个系统中,存在着的大量对象,如果对象有相同属性,并且这个对象是不可变的,那么我们可以将这个对象设计为享元的对象,在内存中只有一个对象,但可以提供给多处代码使用,从而节省内存,实际上,不仅仅是对象可以设计为享元的,对于相似的对象,我们可以将对象中的相同字段提取出来,设计为享元的

我们拿一个简单棋牌游戏来举例

我们在开发一个棋牌游戏的时候,一个大厅内有无数个房间,每个房间都有一个棋盘,每个棋盘上都有对应的棋子,我们需要利用这些数据,来展示出一个完整的棋盘

我们编写了ChessPiece类作为棋子,ChessBoard作为棋盘,那么这个完整的棋局的展现就是如下

public class ChessPiece {//棋子

private int id;

private String text;

private Color color;

private int positionX;

private int positionY;

public ChessPiece(int id, String text, Color color, int positionX, int positionY) {

this.id = id;

this.text = text;

this.color = color;

this.positionX = positionX;

this.positionY = positionX;

}

public static enum Color {

RED, BLACK

}

// …省略其他属性和getter/setter方法…

}

public class ChessBoard {//棋局

private Map<Integer, ChessPiece> chessPieces = new HashMap<>();

public ChessBoard() {

init();

}

private void init() {

chessPieces.put(1, new ChessPiece(1, “車”, ChessPiece.Color.BLACK, 0, 0));

chessPieces.put(2, new ChessPiece(2,”馬”, ChessPiece.Color.BLACK, 0, 1));

//…省略摆放其他棋子的代码…

}

public void move(int chessPieceId, int toPositionX, int toPositionY) {

//…省略…

}

}

那么会出现一个问题,棋子有位置有千万万,但是棋子的个数和颜色还有名称不会变啊,我们没有必要创建如此之多的棋子,于是乎,我们将棋子的颜色名称 id提取出来,形成享元的类

// 享元类

public class ChessPieceUnit {

private int id;

private String text;

private Color color;

public ChessPieceUnit(int id, String text, Color color) {

this.id = id;

this.text = text;

this.color = color;

}

public static enum Color {

RED, BLACK

}

// …省略其他属性和getter方法…

}

public class ChessPieceUnitFactory {

private static final Map<Integer, ChessPieceUnit> pieces = new HashMap<>();

static {

pieces.put(1, new ChessPieceUnit(1, “車”, ChessPieceUnit.Color.BLACK));

pieces.put(2, new ChessPieceUnit(2,”馬”, ChessPieceUnit.Color.BLACK));

//…省略摆放其他棋子的代码…

}

public static ChessPieceUnit getChessPiece(int chessPieceId) {

return pieces.get(chessPieceId);

}

}

public class ChessPiece {

private ChessPieceUnit chessPieceUnit;

private int positionX;

private int positionY;

public ChessPiece(ChessPieceUnit unit, int positionX, int positionY) {

this.chessPieceUnit = unit;

this.positionX = positionX;

this.positionY = positionY;

}

// 省略getter、setter方法

}

public class ChessBoard {

private Map<Integer, ChessPiece> chessPieces = new HashMap<>();

public ChessBoard() {

init();

}

private void init() {

chessPieces.put(1, new ChessPiece(

ChessPieceUnitFactory.getChessPiece(1), 0,0));

chessPieces.put(1, new ChessPiece(

ChessPieceUnitFactory.getChessPiece(2), 1,0));

//…省略摆放其他棋子的代码…

}

public void move(int chessPieceId, int toPositionX, int toPositionY) {

//…省略…

}

}

这样的话,我们所有的棋盘类ChessBoard共享这30个ChessPieceUnit类,在使用的时候,就大大的节省了内存

那么,享元模式的使用很简单了,就是通过工厂模式,在工厂类中,通过一个Map来缓存已经创建好的享元对象,达到复用的目的

那么我们再举一个文本编辑器的例子,如何使用文本编辑器来优化内存占用呢?

因为每一字都可能有着对应的格式大小,那么我们要定一个文字类,来存储字体,大小,颜色,文字信息等

public class Character {//文字

private char c;

private Font font;

private int size;

private int colorRGB;

public Character(char c, Font font, int size, int colorRGB) {

this.c = c;

this.font = font;

this.size = size;

this.colorRGB = colorRGB;

}

}

public class Editor {

private List<Character> chars = new ArrayList<>();

public void appendCharacter(char c, Font font, int size, int colorRGB) {

Character character = new Character(c, font, size, colorRGB);

chars.add(character);

}

}

在文本编辑器中,我们每打一个字,就会创建一个新的Character对象,保存在chars数组中,如果一个文本有成千上万的字,那么就需要存储那么多的Charater对象

于是我们使用了享元模式,将其中的字体,大小,颜色抽取出来

然后只需要直接返回Style对象即可

public class CharacterStyle {

private Font font;

private int size;

private int colorRGB;

public CharacterStyle(Font font, int size, int colorRGB) {

this.font = font;

this.size = size;

this.colorRGB = colorRGB;

}

@Override

public boolean equals(Object o) {

CharacterStyle otherStyle = (CharacterStyle) o;

return font.equals(otherStyle.font)

&& size == otherStyle.size

&& colorRGB == otherStyle.colorRGB;

}

}

public class CharacterStyleFactory {

private static final List<CharacterStyle> styles = new ArrayList<>();

public static CharacterStyle getStyle(Font font, int size, int colorRGB) {

CharacterStyle newStyle = new CharacterStyle(font, size, colorRGB);

for (CharacterStyle style : styles) {

if (style.equals(newStyle)) {

return style;

}

}

styles.add(newStyle);

return newStyle;

}

}

public class Character {

private char c;

private CharacterStyle style;

public Character(char c, CharacterStyle style) {

this.c = c;

this.style = style;

}

}

public class Editor {

private List<Character> chars = new ArrayList<>();

public void appendCharacter(char c, Font font, int size, int colorRGB) {

Character character = new Character(c, CharacterStyleFactory.getStyle(font, size, colorRGB));

chars.add(character);

}

}

那么,根据上面的讲解,这些和单例 缓存 对象池这些概念,有什么区别呢?

享元模式和单例的区别

单例是指,一个类只能创建一个对象,享元模式中,一个类可以创建多个对象,每个对象被多个代码引用共享

类似于多例

但是和多例有区别,多例是为了限制对象的个数,而享元是为了对象的复用

而且这个缓存是为了存储可以复用的对象,和数据库缓存没有啥太大关系

那么享元模式和对象池的区别呢?

对象池和享元模式,都是为了对象的复用,在享元模式中,对象是不可变的,是为了可以重复使用,也可以共享使用,像在对象池中,很多都是被独占的,上锁的,单一线程来使用

发表评论

邮箱地址不会被公开。 必填项已用*标注