I’m modeling a Poker game and I have a little design doubt:
I’ve a PokerHand, which is composed by 5 PokerCards. Should Straight, 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
Cardclass, with a ‘color’ (spades, clubs, diamonds, hearts) and a number (both beingenums); aHandclass, that holds 5 cards (or justncards if you’re into that); an abstractHandRankclass that implements theComparable<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 thatStraightFlushis better thanTwoOfAKind); and a base classAbstractHandAnalyzer. ThisAbstractHandAnalyzerwould have aAnalyze(Hand)method that would return aHandRankobject.Now, you make one subclass of
AbstractHandAnalyzerperHandRanksubclass. Those subclasses check a given hand and return aHandRankinstance if the hand matches (for instance, ifTwoOfAKindAnalyzerfinds that you have two kings, it returns aTwoOfAKindRankthat 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
Handobject, you cannot magically morph it into another class, so subclassingHandto 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 reuseHandand easily implement various analyzers for hands.