I'm modeling a Poker game and I have a little design doubt:
I've a PokerHand
, which is composed by 5 PokerCard
s. Should Straigh开发者_如何学Got
, ThreeOfAKind
, Poker
, etc be subclasses of PokerHand
? Or should they be symbols returned by a PokerHand
method that computes what kind of hand it is?
My rationale for subclassing PokerHand
is that it would make checking winner hands much easier and faster, but I'm not sure this is good engineering...
Is there any design pattern that would fit here?
Thanks!
Last semester, I had to design such a system in Java as a homework. While it was required we checked the hands using the chain of responsibility pattern, I'm fairly sure it was a bad approach to the problem and mostly just a way to plug the pattern into a homework.
If I had to redo it without the chain of responsibility pattern, and using a saner, more cohesive list of strategies, I would use a design like the following.
There would be a Card
class, with a 'color' (spades, clubs, diamonds, hearts) and a number (both being enum
s); a Hand
class, that holds 5 cards (or just n
cards if you're into that); an abstract HandRank
class that implements the Comparable<HandRank>
interface, and subclasses for each kinds of hands (two of a kind, three of a kind, straight, etc) that are each comparable to another (such that StraightFlush
is better than TwoOfAKind
); and a base class AbstractHandAnalyzer
. This AbstractHandAnalyzer
would have a Analyze(Hand)
method that would return a HandRank
object.
Now, you make one subclass of AbstractHandAnalyzer
per HandRank
subclass. Those subclasses check a given hand and return a HandRank
instance if the hand matches (for instance, if TwoOfAKindAnalyzer
finds that you have two kings, it returns a TwoOfAKindRank
that tells it found two kings, and saves the kicker in case it needs it during a comparison later).
All you have to do to analyze a hand, then, is to have a list of hand analyzers in descendant order (so you start with the straight flush), and run each analyzer on the hand until one matches by not returning null
.
The important part here is to decouple the poker hands from the ranks themselves. With most languages (it might not be the case with Smalltalk though), if you construct a Hand
object, you cannot magically morph it into another class, so subclassing Hand
to qualify the ranks can be hard depending on your object instantiation scheme, and can be made next-to-impossible if the hand is mutable (some poker variants allow to swap cards). This approach lets you reuse Hand
and easily implement various analyzers for hands.
PokerHand
should have a method: GetCombination
that returns enum
or object
. Hand is hand, and if player has Straigt it doesn't add any new behavior or state. So combination is calculable from cards.
Edit: I would create class Combination
with these properties:
Type
- enum that represents combination.Player
- ref to player.Cards
- array of ref to involved cards.
Then implement comparison logic, so that any two combinations could be compared: first by Type
then by Cards
(for highest card).
I would probably work with a poker hand as a set (5 presumably) of cards. Then, to check what kind of hand it is, I would create a CalculateValue()
method that returns 1 for pair, 2 for two pairs, 3 for three of a kind, etc. This value could be computed when the hand is constructed, then simply reused whenever it is needed.
Good luck!
I've been thinking about it:
My reasoning is that all hands can be categorized under HighCard
, Pair
, TwoPairs
, ThreeOfAKind
, Straight
, Flush
, FullHouse
, FourOfAKind
and StraightFlush
. All of them are kinds (or classes) of hands, and Hand
is just an abstract class.
All these subclasses just need to override <
and >
, then asking a hand if it's better than another one becomes just as simple as doing aHand < anotherHand
, which looks very natural.
In real life, we would take both hands and compare them by looking first at their kind (class), then (and only if necessary) at the value of their cards. So with this approach, <
and >
would behave exactly as this: check whether the classes are different, in case they are we get the answer automatically. In case they aren't, we recursively check which one has the best cards.
精彩评论