Implementing __init__() in each subclass

As we look at the factory functions for creating Card objects, there are some alternative designs for the Card class. We might want to refactor the conversion of the rank number so that it is the responsibility of the Card class itself. This pushes the initialization down into each subclass.

This often requires some common initialization of a superclass as well as subclass-specific initialization. We need to follow the Don't Repeat Yourself (DRY) principle to keep the code from getting cloned into each of the subclasses.

This version of the Card3 class has an initializer at the superclass level that is used by each subclass, as shown in the following code snippet:

class Card3:

def __init__(
self, rank: str, suit: Suit, hard: int, soft: int
) -> None:
self.rank = rank
self.suit = suit
self.hard = hard
self.soft = soft

class NumberCard3(Card3):

def __init__(self, rank: int, suit: Suit) -> None:
super().__init__(str(rank), suit, rank, rank)

class AceCard3(Card3):

def __init__(self, rank: int, suit: Suit) -> None:
super().__init__("A", suit, 1, 11)

class FaceCard3(Card3):

def __init__(self, rank: int, suit: Suit) -> None:
rank_str = {11: "J", 12: "Q", 13: "K"}[rank]
super().__init__(rank_str, suit, 10, 10)

We've provided __init__() at both the subclass and superclass level. Each subclass uses the super() function to locate the superclass version of __init__(). The superclass version has a number of parameters that can be omitted from the subclass initializers.

This has the small advantage that it simplifies our factory function, as shown in the following code snippet:

def card10(rank: int, suit: Suit) -> Card3:
if rank == 1:
return AceCard3(rank, suit)
elif 2 <= rank < 11:
return NumberCard3(rank, suit)
elif 11 <= rank < 14:
return FaceCard3(rank, suit)
else:
raise Exception("Rank out of range")

We can see from this variation that we've created rather complex __init__() methods for a relatively minor improvement in the simplicity of a factory function. This is a common trade-off. The complexity cannot be removed; it can only be encapsulated. The real question is how should responsibility be allocated for this complexity?

Factory functions encapsulate complexity
There's a trade-off that occurs between sophisticated __init__() methods and factory functions. It's often better to push complex constructors into factory functions. A factory function helps separate construction and initial state-from-state change or other processing concerns.
..................Content has been hidden....................

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