Designing comparisons

There are two considerations when defining comparison operators:

  • The obvious question of how to compare two objects of the same class.
  • The less obvious question of how to compare objects of different classes.

For a class with multiple attributes, we often have a profound ambiguity when looking at the comparison operators. It might not be perfectly clear which of the available attributes participate in the comparison.

Consider the humble playing card (again!). An expression, such as card1 == card2, is clearly intended to compare both rank and suit, right? Is that always true? After all, suit doesn't matter in games such as Blackjack.

If we want to decide whether a Hand object can be split, we must decide whether the split operation is valid. In Blackjack, a hand can only be split if the two cards are of the same rank. The implementation we chose for equality testing will then change how we implement the rules for splitting a hand.

This leads to some alternatives. In one case, the use of rank is implicit; the other requires it to be explicit. The following is the first code snippet for rank comparison:

if hand.cards[0] == hand.cards[1] 

The following is the second code snippet for rank comparison:

if hand.cards[0].rank == hand.cards[1].rank 

While one is shorter, brevity is not always best. If we define equality to only consider rank, we may have trouble creating unit tests. If we use only rank, then assert expectedCard == actualCard will tolerate a wide variety of cards when a unit test should be focused on exactly correct cards.

An expression such as card1 <= 7 is clearly intended to compare rank. Should the ordering operators have slightly different semantics than equality testing?

There are more trade-off questions that stem from a rank-only comparison. How could we order cards by suit if this attribute is not used for ordering comparisons?

Furthermore, equality checks must parallel the hash calculation. If we've included multiple attributes in the hash, we also need to include them in the equality comparison. In this case, it appears that equality (and inequality) between cards must be full Card comparisons, because we're hashing the Card values to include rank and suit.

The ordering comparisons between Card, however, could be rank only. Comparisons against integers could similarly be rank only. For the special case of detecting a split, hand.cards[0].rank == hand.cards[1].rank could be used, because it states the rule for a valid split explicitly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset