设计模式系列篇

设计模式系列篇(十一)——享元模式

2020-08-28  本文已影响0人  复旦猿

What

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式,值得注意的是——享元对象必须是不可变对象。

Why

享元模式的优点有一句话总结为——当系统中存在大量重复对象时,利用享元模式可以大量减少内存占用。
这里,我们重点比较下享元模式和单例模式、缓存、对象池的区别:
享元模式的目标是实现对象复用,节省内存
单例模式是为了保证对象全局唯一
缓存是为了提高访问效率,而非复用
池化技术中的“复用”理解为“重复使用”,主要是为了节省时间

How

享元模式的实现非常简单,主要就是将不可变对象单独通过工厂模式封装起来,在工厂类中,通过一个 Map 或者 List 来缓存已经创建好的享元对象,以达到复用的目的。
今天,你接到一个需求,老板让你开发一个欢乐斗地主的游戏,用户可以创建很多个房间进行游戏。你拿到需求后,就抽象了几个对象出来,扑克牌、房间、用户等。在你设计扑克牌这个对象时,你想到,如果每个房间都要创建扑克牌对象,那房间数很多的时候,扑克牌对象所占用的内存就要爆了啊,所以,你机智的运用了享元模式实现了扑克牌这个类,代码如下:

public class Poker implements Comparable{
    private Integer num;
    private Color color;
    private Shape shape;

    public Poker(Integer num, Color color, Shape shape) {
        this.num = num;
        this.color = color;
        this.shape = shape;
    }

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public Shape getShape() {
        return shape;
    }

    public void setShape(Shape shape) {
        this.shape = shape;
    }

    @Override
    public String toString() {
        return String.format("%s-%s-%s", num, color, shape);
    }

    @Override
    public int compareTo(Object o) {
        Poker poker = (Poker) o;
        if (this == o) {
            return 0;
        }

        if (this.getNum() > poker.getNum()) {
            return 1;
        } else if (this.getNum() < poker.getNum()) {
            return -1;
        } else {
            return 0;
        }
    }

    public enum Color {
        RED("red"),
        BLACK("black");
        private String color;

        public String getColor() {
            return this.color;
        }

        Color(String color) {
            this.color = color;
        }
    }

    public enum Shape {
        DIAMOND("diamond"),

        PLUM("plum"),

        PEACH("peach"),

        JOKER("joker");

        private String shape;

        public String getShape() {
            return this.shape;
        }

        Shape(String shape) {
            this.shape = shape;
        }
    }
}

然后,使用一个工厂类提前创建好扑克牌的对象:

public final class PokerFactory {
    private static final Map<String, Poker> POKER_MAP = new HashMap<>();

    static {
        init();
    }

    public static void init() {
        for (int i = 1; i <= 13; i++) {
            POKER_MAP.put(String.format("%d-red-diamond", i), new Poker(i, Poker.Color.RED, Poker.Shape.DIAMOND));
            POKER_MAP.put(String.format("%d-red-peach", i), new Poker(i, Poker.Color.RED, Poker.Shape.PEACH));
            POKER_MAP.put(String.format("%d-black-plum", i), new Poker(i, Poker.Color.BLACK, Poker.Shape.PLUM));
            POKER_MAP.put(String.format("%d-black-peach", i), new Poker(i, Poker.Color.BLACK, Poker.Shape.PEACH));
        }
        POKER_MAP.put("0-red-joker", new Poker(0, Poker.Color.RED, Poker.Shape.JOKER));
        POKER_MAP.put("0-black-joker", new Poker(0, Poker.Color.BLACK, Poker.Shape.JOKER));
    }

    public static Poker getPoker(String key) {
        if (!POKER_MAP.containsKey(key)) {
            throw new IllegalStateException(String.format("%s does not exist", key));
        }
        return POKER_MAP.get(key);
    }

    public static List<String> getAllPokersString() {
        return new ArrayList<>(POKER_MAP.keySet());
    }
}

init()函数初始化好了所有扑克牌对象,调用getPoker方法就可以获取对应的对象。

接下来,定义一下Room类。

public class Room {
    private String id;

    // save players list;
    private List<String> players;

    // save pokers for each players;
    private Map<String, List<Poker>> pokers;

    public Room(String id, List<String> players) {
        this.id = id;
        this.players = players;
        this.pokers = new HashMap<>(players.size());
    }

    public void dispatch() {
        if (this.players.size() != 3) {
            throw new IllegalStateException("The number of players has to be 3!!!");
        }

        // init
        this.pokers.clear();
        for (String user : players) {
            this.pokers.put(user, new ArrayList<>(13));
        }

        // shuffle
        List<String> keys = PokerFactory.getAllPokersString();
        Collections.shuffle(keys);

        // dispatch
        int count = 0;
        for (String key : keys) {
            this.pokers.get(players.get(count++ % 3)).add(PokerFactory.getPoker(key));
        }
    }

    public void info() {
        System.out.println(String.format("--------Room %s--------\n" +
                "player1: %s \n" +
                "player2: %s \n" +
                "player3: %s \n" +
                "", id, players.get(0), players.get(1), players.get(2)));
        for (String player : players) {
            System.out.println(String.format("Pokers for player %s", player));
            Collections.sort(pokers.get(player));
            for (Poker poker : pokers.get(player)) {
                System.out.println(poker.toString());
            }
            System.out.println();
        }
    }
}

最后,搞个测试类,建个房间,发个牌~

public class TestMain {
    public static void main(String[] args) {
        List<String> users1 = new ArrayList<>();
        users1.add("Jeremy");
        users1.add("Wei");
        users1.add("Min");

        Room room1 = new Room("1", users1);
        room1.dispatch();
        room1.info();

        List<String> users2 = new ArrayList<>();
        users2.add("Tom");
        users2.add("Sam");
        users2.add("Bill");

        Room room2 = new Room("2", users2);
        room2.dispatch();
        room2.info();
    }
}

输出如下:

--------Room 1--------
player1: Jeremy 
player2: Wei 
player3: Min 

Pokers for player Jeremy
0-RED-JOKER
2-RED-PEACH
3-RED-DIAMOND
4-BLACK-PEACH
4-BLACK-PLUM
5-RED-DIAMOND
7-RED-PEACH
7-RED-DIAMOND
8-RED-DIAMOND
10-RED-PEACH
10-RED-DIAMOND
11-RED-PEACH
11-RED-DIAMOND
11-BLACK-PLUM
12-RED-PEACH
12-RED-DIAMOND
13-BLACK-PEACH
13-RED-DIAMOND

Pokers for player Wei
1-RED-DIAMOND
1-BLACK-PEACH
2-RED-DIAMOND
2-BLACK-PEACH
3-BLACK-PEACH
4-RED-DIAMOND
5-BLACK-PLUM
5-RED-PEACH
5-BLACK-PEACH
6-RED-DIAMOND
8-RED-PEACH
9-RED-DIAMOND
9-RED-PEACH
9-BLACK-PLUM
10-BLACK-PLUM
12-BLACK-PLUM
12-BLACK-PEACH
13-RED-PEACH

Pokers for player Min
0-BLACK-JOKER
1-RED-PEACH
1-BLACK-PLUM
2-BLACK-PLUM
3-BLACK-PLUM
3-RED-PEACH
4-RED-PEACH
6-RED-PEACH
6-BLACK-PLUM
6-BLACK-PEACH
7-BLACK-PLUM
7-BLACK-PEACH
8-BLACK-PEACH
8-BLACK-PLUM
9-BLACK-PEACH
10-BLACK-PEACH
11-BLACK-PEACH
13-BLACK-PLUM
...

代码地址

i-learning

写在最后

如果你觉得我写的文章帮到了你,欢迎点赞、评论、分享、赞赏哦,你们的鼓励是我不断创作的动力~

上一篇 下一篇

猜你喜欢

热点阅读