Skip to main content

OOP - Decorator-Pattern

· 4 min read
Strider

The Decorator pattern is a design pattern from Object Oriented Programming. It is intended to facilitate an extension of objects. Let's imagine we have a small ordering platform where we sell cars. Where the cars come from doesn't matter. The cars have to be created as objects in our program.

Why decorator pattern?

Now it can be that some cars have a radio, television etc.... There it makes little sense to create extra attributes in the car class. This is where our Decorator pattern comes into play.

Language used in this uml is german dia.png

We have a base class Car, where everything is derived from. We have here two different Car brands (Tesla and Audi), which inherits from the class Car.

These two brands have no additions, so they are empty. Now comes the Magic. We have a class Decorator, which separates our additions from the cars. This is for a better overview in case the additions require more attributes.

With this we do not clutter up the Car classes. The class Decorator inherits from the base class Car and is therefore of type Car. In the class Decorator the attribute car of the type Car is defined, which later takes over our car.

Now we can create the additions. Here we see once the classes Radio and TV, both inherit from the class Decorator.

Now it should be clear that this pattern can be compared to a matryoshka.

Implementing this pattern

I implemented the whole thing in Python.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import abc

class Car(object):
__metaclass__ = abc.ABCMeta

_price = 0
_feature = ""

@abc.abstractmethod
def getPrice(self):
raise Exception("Not implemented")

@abc.abstractmethod
def getFeature(self):
raise Exception("Not implemented")

The base class Car is to serve as an interface, it is also not to be instantiable. Here all methods as well as the base attributes are defined as prototypes. It's a bit messy to implement attributes in an interface, but it's enough for the example.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from Car import Car

class Audi(Car):

def __init__(self):
self._feature = "Audi R8"
self._price = 120000

def getPrice(self):
return self._price

def getFeature(self):
return self._feature

Here we see our class Audi, which already initializes its equipment (model) and the price in the constructor. The getters are also implemented here.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from Car import Car

class Tesla(Car):

def __init__(self):
self._feature = "Tesla Model 3"
self._price = 50000

def getPrice(self):
return self._price

def getFeature(self):
return self._feature

The class Tesla, is implemented as above the Audi class.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from Car import Car

class Decorator(Car):
_car = None

Here we have our Decorator class, which has only the protected attribute _car. The rest of the stuff it inherits from the Car class.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from Decorator import Decorator

class Radio(Decorator):

def __init__(self, car):
self._car = car

def getPrice(self):
return self._car.getPrice() + 1000.0

def getFeature(self):
return self._car.getFeature() + " with Radio"

Here now have one of the additions. The class Radio, which inherits from the class Decorator, has a passing parameter here in the constructor. Here later our car will be passed, and assigned to the attribute _car. The Magic at this point are the methods, because here our passed car is accessed and extended with further data.

E.g. in the method getPrice, the base price of the car is queried, added with the additional price and returned.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from Decorator import Decorator

class Television(Decorator):

def __init__(self, car):
self._car = car

def getPrice(self):
return self._car.getPrice() + 5000.0

def getFeature(self):
return self._car.getFeature() + " with Television"

The same thing happens with the TV class as with the radio.

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from Audi import Audi
from Tesla import Tesla
from Radio import Radio
from Television import Television

if __name__ == "__main__":
audi = Audi()
radio = Radio(audi)
tv = Television(radio)
print(tv.getFeature() + " Preis: " + str(tv.getPrice()))

If we now test the whole thing, we get the following output

Audi R8 mit Radio mit Ferseher Preis: 126000.0

I hope I could bring someone a little closer to the Decorator pattern 😄