Decorator Design Pattern in Python

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.

What problems can it solve?

- Responsibilities should be added to (and removed from) an object dynamically at run-time

- A flexible alternative to subclassing for extending functionality should be provided.

The UML diagram of the example is given below:

When using subclassing, different subclasses extend a class in different ways. But an extension is bound to the class at compile-time and can't be changed at run-time.

The decorator pattern can be used to extend (decorate) the functionality of a certain object statically, or in some cases at run-time, independently of other instances of the same class.

This is achieved by designing a new Decorator class that wraps the original class. This wrapping could be achieved by the following sequence of steps:

Subclass the original Component class into a Decorator class (see UML diagram);

- In the Decorator class (SpecialistTeacher), add a Component reference as an attribute;

- In the Decorator class, pass a Component to the Decorator constructor to initialize the Component attribute;

- In the Decorator class, forward all Component methods to the Component pointer; and

- In the ConcreteDecorator class (specialistMathTeacher, specialistChemistryTeacher, etc), override any Component method(s) whose behavior needs to be modified.

This pattern is designed so that multiple decorators can be stacked on top of each other, each time adding a new functionality to the overridden method(s).

Note that decorators and the original class object share a common set of features. In the UML diagram, the doWork() and calculateSalary() methods were available in both the decorated and undecorated versions.

Here is the source code of the Decorator Design Pattern in Python:

from abc import ABC, abstractmethod

class Teacher(ABC):
    @abstractmethod
    def calculateSalary(self):
        pass

    def doWork(self):
        pass

class BasicTeacher(Teacher):
    def __init__(self, baseSalaryForATeacher):
        self.baseSalaryForTeacher = baseSalaryForATeacher

    def calculateSalary(self):
        return self.baseSalaryForTeacher

    def doWork(self):
        pass

# Decorator
class SpecialistTeacher(Teacher):
    def __init__(self, teacher):
        self.teacher = teacher

    def calculateSalary(self):
        self.specialistTeacherBaseSalary = self.teacher.calculateSalary()
        return self.specialistTeacherBaseSalary

    def doWork(self):
        super().doWork()
        self.teacher.doWork()

class SpecialistPhysicsTeacher(SpecialistTeacher):
    def __init__(self, specialistTeacher):
        super().__init__(specialistTeacher)

    def calculateSalary(self):
        return super().calculateSalary() + 8000

    def doWork(self):
        super().doWork()
        print("\n I am a specialist Physics Teacher. I teach Physics")

class SpecialistMathsTeacher(SpecialistTeacher):
    def __init__(self, specialistTeacher):
        super().__init__(specialistTeacher)

    def calculateSalary(self):
        return super().calculateSalary() + 10000

    def doWork(self):
        super().doWork()
        print("\n I am a specialist Maths Teacher. I teach Maths")

class SpecialistChemistryTeacher(SpecialistTeacher):
    def __init__(self, specialistTeacher):
        super().__init__(specialistTeacher)

    def calculateSalary(self):
        return super().calculateSalary() + 7000

    def doWork(self):
        super().doWork()
        print("\n I am a specialist Chemistry Teacher. I teach Chemistry")

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    specialistPhyTeacher = SpecialistPhysicsTeacher(SpecialistTeacher(BasicTeacher(10000)))
    specialistPhyTeacher.doWork()
    print("\n My Salary is ", specialistPhyTeacher.calculateSalary())

    specialistphymathsTeacher = SpecialistMathsTeacher(SpecialistPhysicsTeacher(SpecialistTeacher(BasicTeacher(10000))))
    specialistphymathsTeacher.doWork()
    print("\n My Salary is ", specialistphymathsTeacher.calculateSalary())

If we run the above program, the output will look like the following:

I am a specialist Physics Teacher. I teach Physics

My Salary is 18000

I am a specialist Physics Teacher. I teach Physics

I am a specialist Maths Teacher. I teach Maths

My Salary is 28000

0
Subscribe to my newsletter

Read articles from Somenath Mukhopadhyay directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Somenath Mukhopadhyay
Somenath Mukhopadhyay

To win is no more than this... To rise each time you fall...