Calling child class function to reduce code replication

Viewed 41

I'm looking to reduce code replication in my child classes by using common code in the parent, but still need to do some specific processing that changes depending on the child class. I know that calling a child from the parent is bad. How can I accomplish the code reduction? (or should I even be trying?)

This is an example:

class Address:

    def __init__(self, street, city, postal_code):

        self._street = street
        self._city = city
        self._postal_code = self.valid_postal_code(postal_code)

    def valid_postal_code(self, postal_code):
        """ Returns a validated postal code """
        #
        # A big bunch of code common to all postal codes
        #

        if child == Usa:
            return Usa.valid_postal_code(postal_code)
        else:
            return Canada.valid_postal_code(postal_code)    

class Usa(Address):

    def valid_postal_code(self, postal_code):
        """ Returns a validated US zip code """
        # Must be 5 digits or 5 digits plus dash 4 digits
        if len(postal_code) != 5 and len(postal_code) != 10:
            raise Exception("Bad postal code")

        return postal_code


class Canada(Address):

    def valid_postal_code(self, postal_code):
        """ Returns a validates Canadian postal code """
        # Must be A#A #A#
        if len(postal_code) == 6:
            postal_code = postal_code[0:3] + " " + postal_code[3:3]
        if len(postal_code) != 7:
            raise Exception("Bad postal code")

        return postal_code.upper()
2 Answers

Each overrides method should call the parent method.

class Address:

    def __init__(self, street, city, postal_code):

        self._street = street
        self._city = city
        self._postal_code = self.valid_postal_code(postal_code)

    def valid_postal_code(self, postal_code):
        """ Returns a validated postal code """
        #
        # A big bunch of code common to all postal codes
        #
        ...


class Usa(Address):

    def valid_postal_code(self, postal_code):
        """ Returns a validated US zip code """

        # Must be 5 digits or 5 digits plus dash 4 digits
        if not super().valid_postal_code(postal_code) or len(postal_code) != 5 and len(postal_code) != 10:
            raise Exception("Bad postal code")

        return postal_code


class Canada(Address):

    def valid_postal_code(self, postal_code):
        """ Returns a validates Canadian postal code """
        # Must be A#A #A#
        if len(postal_code) == 6:
            postal_code = postal_code[0:3] + " " + postal_code[3:3]
        if not super().valid_postal_code() or len(postal_code) != 7:
            raise Exception("Bad postal code")

        return postal_code.upper()

There are two possibilities:

  • have the parent code contain the common portions and the child contain the specifics
    • parent must return intermediate result

or

  • have two parent methods, one of which is a stub and overridden by the child
    • parent passes intermediate result into stub

The second method looks something like:

class Address:

    def __init__(...):

    def valid_postal_code(self, postal_code):
        """ Returns a validated postal code """
        #
        # A big bunch of code common to all postal codes
        #
        return self._validate_postal_code(postal_code)

    def _validate_postal_code(self, postal_code):
        raise NotImplementedError


class Usa(Address):

    def _validate_postal_code(self, postal_code):
        """ Returns a validated US zip code """
        # Must be 5 digits or 5 digits plus dash 4 digits
        if len(postal_code) != 5 and len(postal_code) != 10:
            raise Exception("Bad postal code")
        return postal_code


class Canada(Address):

    def _validate_postal_code(self, postal_code):
        """ Returns a validates Canadian postal code """
        # Must be A#A #A#
        if len(postal_code) == 6:
            postal_code = postal_code[0:3] + " " + postal_code[3:3]
        if len(postal_code) != 7:
            raise Exception("Bad postal code")
        return postal_code.upper()
Related