Skip to content

Structural Patterns

Decorator Pattern

Definition

The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Design Principle

  • Open-Closed Principle: Classes should be open for extension, but closed for modification.

Note

  • Decorators have the same supertype as the objects they decorate.
  • We can pass around a decorated object in place of the original (wrapped) object.
  • The decorator adds its own behaviour before and/or after delegating to the object it decorates to do the rest of the job.
  • Objects can be decorated at any time, even dynamically at runtime.

Original Diagram

Example codes

from abc import ABC, abstractmethod

# IPizza Component interface
class IPizza(ABC):
    @abstractmethod
    def get_price(self):
        pass

# VeggieMania Concrete component
class VeggieMania(IPizza):
    def get_price(self):
        return 15

# PizzaDecorator Base Decorator
class PizzaDecorator(IPizza, ABC):
    def __init__(self, pizza: IPizza):
        self.pizza = pizza

# TomatoTopping Concrete decorator
class TomatoTopping(PizzaDecorator):

    def get_price(self):
        pizza_price = self.pizza.get_price()
        return pizza_price + 7

# CheeseTopping Concrete decorator
class CheeseTopping(PizzaDecorator):

    def get_price(self):
        pizza_price = self.pizza.get_price()
        return pizza_price + 10

def main():
    pizza = VeggieMania()

    # Add cheese topping
    pizza_with_cheese = CheeseTopping(pizza)

    # Add tomato topping
    pizza_with_cheese_and_tomato = TomatoTopping(pizza_with_cheese)

    print(f"Price of veggeMania with tomato and cheese topping is {pizza_with_cheese_and_tomato.get_price()}")
    # Output: Price of veggeMania with tomato and cheese topping is 32

if __name__ == "__main__":
    main()
package main

import "fmt"

// IPizza Component interface
type IPizza interface {
    getPrice() int
}

// VeggieMania Concrete component
type VeggieMania struct {
}

func (p *VeggieMania) getPrice() int {
    return 15
}

// TomatoTopping Concrete decorator
type TomatoTopping struct {
    pizza IPizza
}

func (c *TomatoTopping) getPrice() int {
    pizzaPrice := c.pizza.getPrice()
    return pizzaPrice + 7
}

// CheeseTopping Concrete decorator
type CheeseTopping struct {
    pizza IPizza
}

func (c *CheeseTopping) getPrice() int {
    pizzaPrice := c.pizza.getPrice()
    return pizzaPrice + 10
}

func main() {

    pizza := &VeggieMania{}

    //Add cheese topping
    pizzaWithCheese := &CheeseTopping{
        pizza: pizza,
    }

    //Add tomato topping
    pizzaWithCheeseAndTomato := &TomatoTopping{
        pizza: pizzaWithCheese,
    }

    fmt.Printf("Price of veggeMania with tomato and cheese topping is %d\n", pizzaWithCheeseAndTomato.getPrice())
    // Output: Price of veggeMania with tomato and cheese topping is 32
}