设计模式之 享元模式

享元模式(Flyweight Pattern),主要作用就是用来共享,共享重复元素,从而达到节省内存的目标。

如果一个类被大量创建使用,并且这个对象本身的属性在使用方那里又不会被改变,那么这个对象就可以被共享,被任意多的类或对象拿去共享。

举例来说,如果让你实现一个五子棋游戏,当一方落一子,这时你是不是new一个黑子或白子,并记录位置呢,那一个棋盘棋子的颜色只有黑白两种,只是位置不同,这时候每个棋子都new一份,一盘棋下来,棋盘的棋子的颜色属性是重复的,就会造成内存的浪费。

这仅仅是一盘棋,如果你要实现一个五子棋游戏大厅,同一时间在线人数达到百万,棋子数可能达到千万甚至几亿数十亿颗,那么这个内存就非常可观了,优化内存也就是必须要做的了。

1、定义共享部分,这里也就是棋子的颜色部分是共享的,所以抽出来作为一个单独的类

package com.itzhimei.study.design.flyweight;

/**
 * @Auther: www.itzhimei.com
 * @Description: 棋子的享元部分
 */
public class ChessPieceFlyweight {

    private ChessPieceColorEnum color;

    public ChessPieceFlyweight(ChessPieceColorEnum color) {
        this.color = color;
    }

    public ChessPieceColorEnum getColor() {
        return color;
    }
}

这里只有get颜色方法,没有set颜色方法,因为颜色是固定的,不应该被修改。

2、定义了棋子颜色枚举类,这里在代码中直接写“白”和“黑”也是可以的

package com.itzhimei.study.design.flyweight;

/**
 * @Auther: www.itzhimei.com
 * @Description: 棋子颜色
 */
public enum ChessPieceColorEnum {

    RED,BLACK
}

3、定义共享部分的生产工厂,防止重复创建

package com.itzhimei.study.design.flyweight;

import java.util.HashMap;
import java.util.Map;

/**
 * @Auther: www.itzhimei.com
 * @Description: 棋子享元工厂类
 */
public class ChessPieceFWFactory {

    private static final Map<Integer, ChessPieceFlyweight> ps = new HashMap();

    static {
        ps.put(1,new ChessPieceFlyweight(ChessPieceColorEnum.RED));
        ps.put(2,new ChessPieceFlyweight(ChessPieceColorEnum.BLACK));
    }

    public static ChessPieceFlyweight getChessPieceFlyweight(int type) {
        return ps.get(type);
    }
}

我们在类中使用静态代码块来实现白子和黑子,只创建一份,并放入map中。

4、棋子类

package com.itzhimei.study.design.flyweight;

/**
 * @Auther: www.itzhimei.com
 * @Description: 其子类
 */
public class ChessPiece {

    //棋子共享部分--颜色
    private ChessPieceFlyweight chessPieceFlyweight;
    //棋子坐标
    private int positionX;
    private int positionY;
    public ChessPiece(ChessPieceFlyweight fw, int positionX, int positionY) {
        this.chessPieceFlyweight = fw;
        this.positionX = positionX;
        this.positionY = positionY;
    }

    @Override
    public String toString() {
        String s = "positionX:" + this.positionX + "---this.positionY:" + this.positionY + "---棋子颜色:" + this.chessPieceFlyweight.getColor();
        return s;
    }
}

棋子类中有棋子的X和Y坐标,并且有共享部分:棋子颜色。

棋子的颜色统一从工厂中获取的,全局只有一份,所以实现了节省内存。

5、测试

package com.itzhimei.study.design.flyweight;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public class Client {

    public static void main(String[] args) {
        for(int i=0; i<10; i++) {
            if(i%2 == 0) {
                ChessPiece chessPiece = new ChessPiece(ChessPieceFWFactory.getChessPieceFlyweight(1), i + 1, i + 1);
                System.out.println(chessPiece);
            } else {
                ChessPiece chessPiece = new ChessPiece(ChessPieceFWFactory.getChessPieceFlyweight(2), i + 1, i + 1);
                System.out.println(chessPiece);
            }
        }
    }
}

输出:

positionX:1---this.positionY:1---棋子颜色:RED
positionX:2---this.positionY:2---棋子颜色:BLACK
positionX:3---this.positionY:3---棋子颜色:RED
positionX:4---this.positionY:4---棋子颜色:BLACK
positionX:5---this.positionY:5---棋子颜色:RED
positionX:6---this.positionY:6---棋子颜色:BLACK
positionX:7---this.positionY:7---棋子颜色:RED
positionX:8---this.positionY:8---棋子颜色:BLACK
positionX:9---this.positionY:9---棋子颜色:RED
positionX:10---this.positionY:10---棋子颜色:BLACK

类关系图:

享元模式的目标就是节省内存,如果有大量重复对象创建,并且需要节省内存,那么你就要考虑使用享元模式了。