Python Design Patterns - Template method pattern


Introduction

Template method pattern is one of the behavioral design patterns. I think that you all must have already used this pattern many times.

In this pattern, Abstract class holds the primitive operations which needs to be executed and all those methods might be abstract if they are customizable and it also holds one method which executes all the other methods in pattern which you have defined. This method is also called template method. This abstract class must be overridden by sub classes to write custom primitive operations according to your requirements.

We can understand this pattern by following code.

import abc

class AbstractClass(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def operation_1(self):
        pass

    @abc.abstractmethod
    def operation_1(self):
        pass

    @abc.abstractmethod
    def template_method(self):
        self.operation_1()
        self.operation_2()

class RealClass1(AbstractClass):
    @abc.abstractmethod
    def operation_1(self):
        print("Real operation 1)

    @abc.abstractmethod
    def operation_1(self):
        print("Real operation 1)

obj = RealClass1()
obj.template_method()

# Output :
# Real operation 1
# Real operation 2

In this sample code, You can see AbstractClass holds two operation method which are abstract methods and one template_method which executes both the operations. Now if you see RealClass1 overrides two operation methods according to requirements. Further, if you create object of RealClass1 and call template_method method which will execute both operations which are written in sub class. That way you can build many other sub classes as per different requirements.

Now let's understand with real life example of trip planning.

We have one trip planner and he has multiple packages of three days trips. Now how he create the itinerary for the customers. Suppose customer wants to go south or north then according to that planner will give him the package. Now lets see how he plans/executes without doing duplicate work. Planner has already one base format where he needs to mention how traveler will travel to destination, what they will do there on day 1,2 and 3 and at last how they will travel to back home.

Let's write base class ThreeDaysTrip which already knows how to execute the trip.

import abc

class ThreeDaysTrip(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def transport(self):
        pass

    @abc.abstractmethod
    def day1(self):
        pass

    @abc.abstractmethod
    def day2(self):
        pass

    @abc.abstractmethod
    def day3(self):
        pass

    @abc.abstractmethod
    def back_to_home(self):
        pass

    def itinerary(self):
        print("Trip is started")
        self.transport()
        self.day1()
        self.day2()
        self.day3()
        self.back_to_home()
        print("Trip is over")

By extending this base class, we are going to write two more class with different locations and different itinerary which will be used to execute two different trips.

class SouthTrip(ThreeDaysTrip):
    def transport(self):
        print("Go by train! check in to hotel")

    def day1(self):
        print("Day-1: Enjoy the hotel beach whole day")

    def day2(self):
        print("Day-2: Visit historical places and Enjoy cruise life at night")

    def day3(self):
        print("Day-3: Enjoy shopping day with family and go anywhere you wish")

    def back_to_home(self):
        print("Check out and go Home by air!")


class NorthTrip(ThreeDaysTrip):
    def transport(self):
        print("Go by air! check in to hotel")

    def day1(self):
        print("Day-1: Go to very highted place and enjoy snow activities")

    def day2(self):
        print("Day-2: Enjoy river rafting and lavish dinner at night")

    def day3(self):
        print("Day-3: Enjoy shopping day with family and go anywhere you wish")

    def back_to_home(self):
        print("Check out and go Home by air!")

In above code, You can see two classes NorthTrip and SouthTrip both are three days trip but process is very much similar of executing the whole trip. So all the abstract methods are overridden as per need of both the trips. This way if you are planning to start new trip in different direction then you need to write one more class and just add the itinerary.

Now let's look at the planners code how that will use above classes.

if __name__ == "__main__":
    place = input("Where do you want to go? ")
    if place == 'north':
        trip = NorthTrip()
        trip.itinerary()
    elif place == 'south':
        trip = SouthTrip()
        trip.itinerary()
    else:
        print("Sorry, We do not have any trip towards that place!")

As you can see, planner is asking for the place where customer wants to go and according to that he calls the different class. If custom wants to go south side then SouthTrip class is used. But if trip is not available the he straight forward will say sorry. :)

Certainly there are benefits of this pattern so let's look at the advantages.

Advantages

  • Reduction of code duplication
  • As it uses inheritance, reusability of the code increases
  • Steps execution of the process can be customized

When you see advantages of anything, there are always none to minor disadvantages along with it.

Disadvantages

  • As process execution is hold by base class, with more and more changes in the program maintainability becomes difficult.
  • Sometimes, debugging becomes hard as execution is always happen in base class so writing better error handling can help.

I hope you have understood the pattern and its purpose. Thanks!

blog comments powered by Disqus