OOD Interview Questions
设计 Deck of Cards
Deck of Cards 的面向对象设计示例
这份 notebook 由 Donne Martin 准备。Source 和 license info 在 GitHub。
设计 Deck of Cards
Constraints & assumptions
- 这是一个通用的 deck(用于 poker、blackjack 等)?
- Yes,先设计 generic deck,再扩展到 blackjack
- 可以假设 deck 有 52 张牌 + 4 suits?
- Yes
- 输入可以假设 valid 吗?
- Assume they're valid
Solution
设计思路:
- 先抽象出 Card 和 Deck,保证适用于各种 card game
- 再通过继承扩展 BlackJackCard 和 BlackJackHand
核心对象
Suit:HEART / DIAMOND / CLUBS / SPADECard(abstract):value + suit + is_availableBlackJackCard:把 A、J/Q/K 规则封装到 value 计算Hand:持牌集合,基础 scoreBlackJackHand:处理 A 的多种计分方式Deck:持有所有 cards,支持 shuffle / deal
Class 设计(示例)
class Suit(Enum):
HEART = 0
DIAMOND = 1
CLUBS = 2
SPADE = 3
class Card(metaclass=ABCMeta):
def __init__(self, value, suit):
self.value = value
self.suit = suit
self.is_available = True
class BlackJackCard(Card):
def is_ace(self):
return True if self._value == 1 else False
def is_face_card(self):
return True if 10 < self._value <= 13 else False
@property
def value(self):
if self.is_ace():
return 1
elif self.is_face_card():
return 10
return self._value
class Hand(object):
def __init__(self, cards):
self.cards = cards
def add_card(self, card):
self.cards.append(card)
def score(self):
return sum(card.value for card in self.cards)
class BlackJackHand(Hand):
BLACKJACK = 21
def possible_scores(self):
# 处理 A: 1 或 11
pass
def score(self):
# 选 <= 21 的最大值,否则最小超出值
pass
class Deck(object):
def __init__(self, cards):
self.cards = cards
self.deal_index = 0
def remaining_cards(self):
return len(self.cards) - self.deal_index
def deal_card(self):
try:
card = self.cards[self.deal_index]
card.is_available = False
self.deal_index += 1
return card
except IndexError:
return None
def shuffle(self):
pass
Blackjack 的核心点
- A 可以是 1 或 11,所以需要计算多个 possible scores
- 结果选择规则:
- 如果有 <= 21 的分数,取最大
- 否则取最小超出值
可以扩展的点
- Deck factory:根据 game 生成不同 deck(含 Jokers 等)
- Shuffle 策略:Fisher–Yates
- Multiple decks:casino blackjack 多副牌
- Game engine:turn-based 状态机