设计模式系列篇(十一)——享元模式
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
...
代码地址
写在最后
如果你觉得我写的文章帮到了你,欢迎点赞、评论、分享、赞赏哦,你们的鼓励是我不断创作的动力~