享元模式是八种结构型模式之一,享元就是共享单元(元素)的意思。单元代表了整体的部分,如果一个对象其中的部分是稳定的不会改变的,那么这个部分即可以共享。享元模式的用途就是复用对象的单元,让对象的节省内存。
后面用扑克牌举例如何使用享元模式。
斗地主相信很多人都玩过,一副版,黑红花方四色,每一色都有从2到A的13张牌,加上大小王,共有54张牌。假如一张牌就是一个对象,一张桌子就要54个对象;一个牌室300张桌子;一个服务器有50个牌室;这样的服务器可能又有200个。那这样的对象就是54*300*50*200=162000000个。
/**
* 花色
*/
public enum Color {
/**
* 红心
*/
heart,
/**
* 黑桃
*/
spade,
/**
* 梅花
*/
club,
/**
* 方块
*/
diamond,
/**
* 大小王
*/
joker
}
/**
* 一张牌
*/
public class Card {
/**
* 编号,用于牌比较大小,1-13,小王14,大王15
*/
private int num;
/**
* 名称
*/
private String name;
/**
* 花色
*/
private Color color;
/**
* 出牌状态
*/
Card(int num, String name, Color color){
this.num = num; this.name = name; this.color = color;
}
/**
* 出牌
*/
public void play(){
this.status = true;
}
/**
* 洗牌
*/
public void shuffle(){
this.status = false;
}
@Override
public String toString() {
return "Card{" +
"num=" + num +
", name='" + name + '\'' +
", color=" + color +
'}';
}
}
/**
* 一副牌
*/
public class Deck {
List<Card> cardList;
Deck(){
cardList = new ArrayList<>(54);
cardList.add(new Card(1,"2",Color.spade));//黑桃2
...省略中间的牌
cardList.add(new Card(13,"A",Color.spade));//黑桃A
cardList.add(new Card(1,"2",Color.heart));//红星2
...省略中间的牌
cardList.add(new Card(13,"A",Color.heart));//红星A
cardList.add(new Card(1,"2",Color.club));//梅花2
...省略中间的牌
cardList.add(new Card(13,"A",Color.club));//梅花A
cardList.add(new Card(1,"2",Color.diamond));//方块2
...省略中间的牌
cardList.add(new Card(13,"A",Color.diamond));//方块A
cardList.add(new Card(14,"小王",Color.joker));//小王
cardList.add(new Card(15,"大王",Color.joker));//大王
}
/**
* 洗牌
*/
public void shuffle(){
Card[] objects = this.cardList.stream().toArray(Card[]::new);
int size = this.cardList.size();
//把牌顺序打乱
for(int i = 0; i < size; i++){
int currentRandom = (int)(Math.random() * (size - 1));
Card current = objects[i];
objects[i] = objects[currentRandom];
objects[currentRandom] = current;
}
this.cardList.clear();
this.cardList= Arrays.asList(objects);
//设置牌状态为未出牌
this.cardList.forEach(card -> {
card.shuffle();
System.out.println(card.toString());
});
}
}
打过牌的人都知道每一幅牌里的牌都是一样的。不管哪幅牌,牌的数量和同样一张牌的编号、名称、花色都是一样的且不会变化,所以可以提取这些单元做为共享单元,让所有牌共享。
/**
* 一张牌的共享单元
*/
public class CardUnit {
/**
* 编号,用于牌比较大小,1-13,小王14,大王15
*/
private int num;
/**
* 名称
*/
private String name;
/**
* 花色
*/
private Color color;
CardUnit(int num, String name, Color color){
this.num = num; this.name = name; this.color = color;
}
@Override
public String toString() {
return "CardUnit{" +
"num=" + num +
", name='" + name + '\'' +
", color=" + color +
'}';
}
}
/**
* 共享单元的工厂类
*/
public class CardUnitFactory {
private static final Map<Integer,CardUnit> cardUnitMap = new HashMap<>(54);
static {
cardUnitMap.put(1, new CardUnit(1,"2",Color.spade));//黑桃2
//....
cardUnitMap.put(13, new CardUnit(13,"A",Color.spade));//黑桃A
cardUnitMap.put(14, new CardUnit(1,"2",Color.heart));//红星2
//....
cardUnitMap.put(26, new CardUnit(13,"A",Color.heart));//红星A
cardUnitMap.put(27, new CardUnit(1,"2",Color.club));//梅花2
//....
cardUnitMap.put(39, new CardUnit(13,"A",Color.club));//梅花A
cardUnitMap.put(40, new CardUnit(1,"2",Color.diamond));//方块2
//....
cardUnitMap.put(52, new CardUnit(13,"A",Color.diamond));//方块A
cardUnitMap.put(53, new CardUnit(14,"小王",Color.joker));//小王
cardUnitMap.put(54, new CardUnit(15,"大王",Color.joker));//大王
}
public static CardUnit getCardUnit(Integer id) { return cardUnitMap.get(id); }
}
/**
* 一张牌
*/
public class Card {
private CardUnit cardUnit;
/**
* 出牌状态
*/
private boolean status;
Card(CardUnit cardUnit){
this.cardUnit = cardUnit;
}
/**
* 出牌
*/
public void play(){
this.status = true;
}
/**
* 洗牌
*/
public void shuffle(){
this.status = false;
}
@Override
public String toString() {
return "Card{" +
"cardUnit=" + cardUnit +
", status=" + status +
'}';
}
}
/**
* 一副牌
*/
public class Deck {
List<Card> cardList;
Deck(){
cardList = new ArrayList<>(54);
cardList.add(new Card(CardUnitFactory.getCardUnit(1)));//黑桃2
//...省略中间的牌
cardList.add(new Card(CardUnitFactory.getCardUnit(13)));//黑桃A
cardList.add(new Card(CardUnitFactory.getCardUnit(14)));//红星2
//...省略中间的牌
cardList.add(new Card(CardUnitFactory.getCardUnit(26)));//红星A
cardList.add(new Card(CardUnitFactory.getCardUnit(27)));//梅花2
//...省略中间的牌
cardList.add(new Card(CardUnitFactory.getCardUnit(39)));//梅花A
cardList.add(new Card(CardUnitFactory.getCardUnit(40)));//方块2
//...省略中间的牌
cardList.add(new Card(CardUnitFactory.getCardUnit(52)));//方块A
cardList.add(new Card(CardUnitFactory.getCardUnit(53)));//小王
cardList.add(new Card(CardUnitFactory.getCardUnit(54)));//大王
}
/**
* 洗牌
*/
public void shuffle(){
Card[] objects = this.cardList.stream().toArray(Card[]::new);
int size = this.cardList.size();
//把牌顺序打乱
for(int i = 0; i < size; i++){
int currentRandom = (int)(Math.random() * (size - 1));
Card current = objects[i];
objects[i] = objects[currentRandom];
objects[currentRandom] = current;
}
this.cardList.clear();
this.cardList= Arrays.asList(objects);
//设置牌状态为未出牌
this.cardList.forEach(card -> {
card.shuffle();
System.out.println(card.toString());
});
}
}
总结
共享模式就是把对象中稳定不变化的单元,提取出来做为共享单元,并使用工厂类来创建共享单元。