CodeBlanks Logo Icon CodeBlanks

Decorator

Learn the Decorator pattern.

Decorator (design pattern)

The Decorator design pattern extends an object's behavior by wrapping it. It is different from Python's @decorator syntax; here a wrapper class holds the inner object and calls methods through it.

class Coffee:
    def cost(self):
        return 10

class MilkDecorator:
    def __init__(self, coffee):
        self._coffee = coffee
    def cost(self):
        return self._coffee.cost() + 2

The Decorator adds new behavior without changing the wrapped object; for example, milk, sugar and other add-ons are layered onto a drink.

Layered decorators

Decorators can be layered; each layer wraps the previous one. This allows adding behavior dynamically.

class SugarDecorator:
    def __init__(self, coffee):
        self._coffee = coffee
    def cost(self):
        return self._coffee.cost() + 1

c = SugarDecorator(MilkDecorator(Coffee()))
print(c.cost())  # 10 + 2 + 1 = 13

This is an alternative to subclassing; you create many combinations without increasing the number of classes.

Practice Coffee cost with Decorator

Define class Coffee: with def cost(self): returning 10. Define class MilkDecorator: with def __init__(self, coffee): doing self._coffee = coffee. Method def cost(self): returns self._coffee.cost() + 2. Create c = MilkDecorator(Coffee()). Print the cost with print(c.cost()).

Starter code

main.py
# define Coffee class (cost = 10)
# define MilkDecorator class (store inner, cost + 2)
# wrap a Coffee instance with MilkDecorator
# print the cost

Sample solution

main.py
class Coffee:
    def cost(self):
        return 10

class MilkDecorator:
    def __init__(self, coffee):
        self._coffee = coffee
    def cost(self):
        return self._coffee.cost() + 2

c = MilkDecorator(Coffee())
print(c.cost())

Expected output

12

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Complete the Decorator __init__ body

Question
class MilkDecorator:
    def __init__(self, coffee):
        self.____ = coffee
Answer
class MilkDecorator:
    def __init__(self, coffee):
        self._coffee = coffee

Explanation: self._coffee = coffee stores the inner object.

2 What does MilkDecorator.cost() return in the Decorator pattern?

  • It raises an error
  • Just 2
  • Just the inner object's cost
  • Inner object's cost + 2 Correct answer

Explanation: self._coffee.cost() + 2 calls the inner and adds.

3 Match the Decorator concepts

Term Meaning
MilkDecorator Is the wrapper
self._coffee Is the inner object
cost() + 2 Adds behavior
layering Dynamic extension

All lessons in Advanced