扑克牌的场景,我们可以分析扑克牌的特点来设计享元模式。扑克牌有 4 种花色(黑桃、红桃、梅花、方块)和 13 种牌面(A、2 - 10、J、Q、K),还有大小王。花色和牌面可以看作内部状态进行共享,而扑克牌在游戏中的具体位置、是否被打出等信息可作为外部状态。
以下的代码:
import java.util.HashMap;
import java.util.Map;
// 抽象享元角色:扑克牌接口
interface PokerCard {
void displayCard(String externalState);
}
// 具体享元角色:扑克牌
class ConcretePokerCard implements PokerCard {
private String suit; // 花色
private String rank; // 牌面
public ConcretePokerCard(String suit, String rank) {
this.suit = suit;
this.rank = rank;
}
@Override
public void displayCard(String externalState) {
System.out.println("花色: " + suit + ", 牌面: " + rank + ", 外部状态: " + externalState);
}
}
// 享元工厂角色:扑克牌工厂
class PokerCardFactory {
private static final Map<String, PokerCard> cardPool = new HashMap<>();
public static PokerCard getCard(String suit, String rank) {
String key = suit + "-" + rank;
PokerCard card = cardPool.get(key);
if (card == null) {
card = new ConcretePokerCard(suit, rank);
cardPool.put(key, card);
}
return card;
}
}
// 客户端代码
public class PokerGame {
public static void main(String[] args) {
String[] suits = {"黑桃", "红桃", "梅花", "方块"};
String[] ranks = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
// 生成普通扑克牌
for (String suit : suits) {
for (String rank : ranks) {
PokerCard card = PokerCardFactory.getCard(suit, rank);
card.displayCard("在牌堆中");
}
}
// 处理大小王
PokerCard smallJoker = PokerCardFactory.getCard("小王", "");
smallJoker.displayCard("在牌堆中");
PokerCard bigJoker = PokerCardFactory.getCard("大王", "");
bigJoker.displayCard("在牌堆中");
}
}
代码解释
- 抽象享元角色(
PokerCard
接口):定义了displayCard
方法,用于展示扑克牌的信息,其中externalState
为外部状态。 - 具体享元角色(
ConcretePokerCard
类):实现了PokerCard
接口,包含了扑克牌的花色suit
和牌面rank
作为内部状态。displayCard
方法将内部状态和传入的外部状态一起输出。 - 享元工厂角色(
PokerCardFactory
类):维护一个cardPool
用于存储已经创建的扑克牌对象。getCard
方法根据传入的花色和牌面生成一个唯一的键,在cardPool
中查找对应的扑克牌对象。如果对象不存在,则创建一个新的ConcretePokerCard
对象并放入cardPool
中,最后返回该对象。 - 客户端代码(
PokerGame
类):通过双重循环生成 52 张普通扑克牌,调用PokerCardFactory
的getCard
方法获取扑克牌对象,并传入外部状态(这里是“在牌堆中”)展示扑克牌信息。另外,单独处理大小王。
通过这种方式,我们使用享元模式避免了为 54 张扑克牌都创建新对象,而是共享相同花色和牌面的扑克牌对象,节省了内存。