Max Segal
Max Segal

Reputation: 2055

Generalize class method that does slightly different actions according to condition

I have a simplified class Animal that can do food_routine in which the actions differ depending on the type of the animal:

DOG = 'dog'
CAT = 'cat'

class Animal(object):
    def __init__(self, animal_type):
        if animal_type not in [CAT, DOG]:
            raise Exception('unexpected animal')
        self.animal_type = animal_type

    def food_routine(self):
        self.come_to_plate()  # common code
        self.sniff_food()  # common code

        if self.animal_type == CAT:
            self.go_around_plate()

        self.eat_the_food()  # common code

        if self.animal_type == DOG:  # condition I want to avoid
            self.thank_owner()

        self.go_away_from_plate()  # common code

        # cats and dogs do different things after eating:
        if self.animal_type == CAT:  # condition I want to avoid
            self.go_lie_on_couch()
        elif self.animal_type == DOG:  # condition I want to avoid
            self.ask_for_walk_outside()

        self.sleep()  # common code

    def sleep(self):
        print 'sleep'  # common code

    def ask_for_walk_outside(self):
        print 'ask for a walk outside'

    def go_lie_on_couch(self):
        print 'go lie on couch'

    def go_away_from_plate(self):
        print 'go away from plate'  # common code

    def thank_owner(self):
        print 'thank owner'  # only dogs thank owner

    def eat_the_food(self):
        print 'eat the food'  # common code

    def go_around_plate(self):
        print 'go around plate'

    def sniff_food(self):
        print 'sniff food'  # common code

    def come_to_plate(self):
        print 'come to plate'  # common code

My problem is the if statements that make the code unreadable. I tried to solve it by creating two classes: one for Dog and one for Cat. Each animal only does things he knows to do in food_routine (and removed the method from Animal):

class Cat(Animal):
    def food_routine(self):
        self.come_to_plate()  # common code
        self.sniff_food()  # common code
        self.go_around_plate()
        self.eat_the_food()  # common code
        self.go_away_from_plate()  # common code
        self.go_lie_on_couch()
        self.sleep()  # common code


class Dog(Animal):
    def food_routine(self):
        self.come_to_plate()  # common code
        self.sniff_food()  # common code
        self.eat_the_food()  # common code
        self.thank_owner()
        self.go_away_from_plate()  # common code
        self.ask_for_walk_outside()
        self.sleep()  # common code

This results in code duplication (common code lines) which I want to avoid.

So my question is: What is a good practice to avoid code duplication but also to avoid the if statements so the code is readable and simple?

Upvotes: 2

Views: 84

Answers (2)

TzurEl
TzurEl

Reputation: 862

The food_routine should be mutual.

Divide your actions into phases - "pre action", "action", "post action". Each class should implement the "pre action" functions and "post action" functions, and the Animal class should contain all the common code. Each dog/cat should call the super "pre" and "post" in order to avoid code duplication.

class Animal(object):
    .
    .
    .
    def action():
        self.eat_the_food()  # common code

    def pre_action():
        self.come_to_plate()  # common code
        self.sniff_food()  # common code

    def post_action():
        self.go_away_from_plate()  # common code
        self.sleep()  # common code


class Cat(Animal):
    .
    .
    def pre_action():
        super().pre_action()
        self.go_around_plate()


class Dog(Animal)
    .
    .
    .
    def post_action():
        self.thank_owner()
        self.ask_for_walk_outside()
        self.super().post_action()

And then, each call can be:

Dog.pre_action()
Dog.action()
Dog.post_action()

Upvotes: 2

Eric
Eric

Reputation: 97691

How about:

def food_routine(self):
    self.come_to_plate()  # common code
    self.sniff_food()  # common code

    self._pre_eat_action()

    self.eat_the_food()  # common code

    self._post_eat_action()

    self.go_away_from_plate()  # common code

    self._post_leave_plate_action()

    self.sleep()  # common code

Then implement _pre_eat_action, _post_eat_action, and _post_leave_plate_action in both Dog and Cat to handle the two different behaviors.

Upvotes: 2

Related Questions