Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Polymorphism

One of the four important features of class definition is polymorphism. Polymorphism exists when you define a number of subclasses which have commonly named methods. A function can use objects of any of the polymorphic classes without being aware that the classes are distinct.

In some languages, it is essential that the polymorphic classes have the same interface (or be subinterfaces of a common parent interface), or be subclasses of a common superclass. This is sometimes called "strong, hierarchical typing", since the type rules are very rigid and follow the subclass/subinterface hierarchy.

Python implements something that is less rigid, often called "duck typing". The phrase follows from a quote attributed to James Whitcomb Riley: "When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck." In short, two objects are effectively of the class Duck if they have a common collection of methods (walk, swim and quack, for example.)

When we look at the examples for Card, FaceCard, Ace in the section called “Inheritance”, we see that all three classes have the same method names, but have different implementations for some of these methods. These three classes are polymorphic. A client class like Hand can contain individual objects of any of the subclasses of Card. A function can evaluate these polymorphic methods without knowing which specific subclass is being invoked.

In our example, both FaceCard and Ace were subclasses of Card. This subclass relationship isn't necesary for polymorphism to work correctly in Python. However, the subclass relationship is often an essential ingredient in an overall design that relies on polymorphic classes.

What's the Benefit? If we treat all of the various subclasses of Card in a uniform way, we effectively delegate any special-case processing into the relevant subclass. We concentrate the implementation of a special case in exactly one place.

The alternative is to include if -statements all over our program to enforce special-case processing rules. This diffusing of special-case processing means that many components wind up with an implicit relationship. For example, all portions of a program that deal with Cards would need multiple if -statements to separate the number card points, face card points and ace points.

By making our design polymorphic, all of our subclasses of Card have ranks and suits, as well as hard and soft point values. We we can design the Deck and Shoe classes to deal cards in a uniform way. We can also design a Hand class to total points without knowing which specific class to which a Card object belongs.

Similarly, we made our design for Deck and Shoe classes polymorphic. This allows us to model one-deck blackjack or multi-deck blackjack with no other changes to our application.

The Hand of Cards. In order to completely model Blackjack, we'll need a class for keeping the player and dealer's hands. There are some differences between the two hands: the dealer, for example, only reveals their first card, and the dealer can't split. There are, however, some important similarities. Every kind of Hand must determine the hard and soft point totals of the cards.

The hard point total for a hand is simply the hard total of all the cards. The soft total, on the other hand, is not simply the soft total of all cards. Only the Ace cards have different soft totals, and only one Ace can meaningfully contribute it's soft total of 11. Generally, all cards provide the same hard and soft point contributions. Of the cards where the hard and soft values differ, only one such card needs to be considered.

Note that we are using the values of the getHardValue and getSoftValue methods. Since this test applies to all classes of cards, we preserve polymorphism by checking this property of every card. We'll preserving just one of the cards with a soft value that is different from the hard value. At no time do use investigate the class of a Card to determine if the card is of the class Ace. Examining the class of each object needlessly constrains our algorithm. Using the polymorphic methods means that we can make changes to the class structure without breaking the processing of the Hand class.

Pretty Poor Polymorphism

The most common indicator of poor use polymorphism is using the type, isinstance and issubclass functions to determine the class of an object. These should used rarely, if at all. All processing should be focused on what is different about the objects, not the class to which an object belongs.

We have a number of ways to represent the presence of a Card with a distinct hard and soft value.

  • An attribute with the point difference (usually 10).

  • A collection of all Cards except for one Card with a point difference, and a single attribute for the extra card.

We'll choose the first implementation. We can use use a sequence to hold the cards. When cards are added to the hand, the first card that returns distinct values for the hard value and soft value will be used to set a variable has keeps the hard vs. soft point difference.

Example 22.2. hand.py

class Hand( object ):
    """Model a player's hand."""
    def __init__( self ):
        self.cards = [ ]
        self.softDiff= 0
    def addCard( self, aCard ):
        self.cards.append( aCard )
        if aCard.getHardValue() != aCard.getSoftValue():
            if self.softDiff == 0:
                self.softDiff= aCard.getSoftValue()-aCard.getHardValue()
    def points( self ):
        """Compute the total points of cards held."""
        p= 0
        for c in self.cards:
            p += c.getHardValue()
        if p + self.softDiff <= 21:
            return p + self.softDiff
        else:
            return p
1

The __init__ special function creates the instance variable, self.cards, which we will use to accumulate the Card objects that comprise the hand. This also sets self.softDiff which is the difference in points between hard and soft hands. Until we have an Ace, the difference is zero. When we get an Ace, the difference will be 10.

2

We provide an addCard method that places an additional card into the hand. At this time, we examine the Card to see if the soft value is different from the hard value. If so, and we have not set the self.softDiff yet, we save this difference.

3

The points method evaluates the hand. It initializes the point count, p, to zero. We start a for -loop to assign each card object to c. We could, as an alternative, use a reduce function to perform this operation: reduce( lambda a,b:a+b.getSoftValue(), self.cards, 0 ).

If the total with the self.softDiff is 21 or under, we have a soft hand, and these are the total points. If the total with the self.softDiff is over 21, we have a hard hand. The hard hand may total more than 21, in which case, the hand is bust.


 
 
  Published under the terms of the Open Publication License Design by Interspire