Definition

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

Explanation

You have a base interface or class (e.g. Beverage) and concrete implementations (Espresso, Tea). Instead of subclassing every possible combination (e.g. EspressoWithMilk), you create Decorator classes that wrap a Beverage and add behavior (e.g. MilkDecorator). Multiple decorators can wrap an object at runtime to accumulate behaviors. Each decorator has the same interface, so it’s transparent to clients.

Code

Here, ConcreteComponent is wrapped by decorators. ConcreteDecoratorA and ConcreteDecoratorB extend the behavior of ConcreteComponent without modifying its code. The client_code function shows how a component and its decorated versions behave.
class Component:
    def operation(self) -> str:
        pass

class ConcreteComponent(Component):
    def operation(self) -> str:
        return "ConcreteComponent"

class Decorator(Component):
    def __init__(self, component: Component) -> None:
        self._component = component

    @property
    def component(self) -> Component:
        return self._component

    def operation(self) -> str:
        return self._component.operation()

class ConcreteDecoratorA(Decorator):
    def operation(self) -> str:
        return f"ConcreteDecoratorA({self.component.operation()})"

class ConcreteDecoratorB(Decorator):
    def operation(self) -> str:
        return f"ConcreteDecoratorB({self.component.operation()})"

def client_code(component: Component) -> None:
    print(f"RESULT: {component.operation()}", end="")

def main():
    simple = ConcreteComponent()
    print("Client: I've got a simple component:")
    client_code(simple)
    print("\n")

    decorator1 = ConcreteDecoratorA(simple)
    decorator2 = ConcreteDecoratorB(decorator1)
    print("Client: Now I've got a decorated component:")
    client_code(decorator2)
    print()

if __name__ == "__main__":
    main()

Analogy

Adding condiments to a coffee order or layers to an outfit. E.g., Christmas tree: you start with a plain tree and attach ornaments (decorators) like lights, tinsel, star, each adding functionality (light, color). Or a Window with scrollbars: you start with a base window and add horizontal or vertical scroll decorator to extend its behavior.

Interview Insights

Use Case: When you want to add responsibilities to objects at runtime. UI component styling (e.g. Swing’s JScrollPane wraps a component), I/O streams (e.g. Java’s BufferedInputStream decorating a FileInputStream), or adding features (e.g. text formatting). Helps avoid “explosion” of subclasses.
Advantages: Provides more flexibility than static inheritance. You can combine decorators in any order/quantity. Adheres to Single Responsibility: each decorator focuses on one feature.
Disadvantages: A large number of small decorator classes can complicate design. Hard to configure in code if many decorators are needed. Wrapping layers can be confusing to debug (many levels).
Common questions: “Difference between Decorator and Adapter/Proxy?”, “Why use Decorator instead of inheritance?”, “Show example (e.g. coffee with toppings)”, “What is the effect on existing code if we want to add a new feature (decorator vs subclass)?”.\