Skip to content

Week 4 - Decorator Design Pattern

The Decorator Design Pattern

Classification

  • Structural Design Pattern

Pattern Definition

  • Attach additional responsibilities to an object dynamically.
    • Think of a decorator WRAPPING around some other object or method.
  • Decorators provide a flexible alternative to sub-classing for extending functionality.
  • Open/Close Principle: Open for extension and closed to modification.
    • Decorator allows for this seemingly contradictory goals, but be CHOOSY when you use it because it can lead to costly, hard-to-understand code.
  • The decorator adds its own behavior before and/or after delegating to the object it decorates to do the rest of the job.

Representations

Mermaid Graph

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
classDiagram
    ConcreateComponent ..|> Component : implements
    ComponentDecorator ..|> Component : implements
    ConcreateDecoratorA --|> ComponentDecorator : extends
    ConcreateDecoratorB --|> ComponentDecorator : extends
    ComponentDecorator *-- Component : composition
    class Component{
        <<interface>>
        +methodA()
        +methodB()
    }
    class ConcreateComponent{
        +ConcreateComponent()
        +methodA()
        +methodB()
    }
    class ComponentDecorator{
        Component wrappedComponent
        ComponentDecorator(Component)
        +methodA()
        +methodB()
    }
    class ConcreateDecoratorA{
        +ConcreateDecoratorA(Component)
        +methodA()
        +methodB()
        +newBehavior()
    }
    class ConcreateDecoratorB{
        Object newState
        +ConcreateDecoratorB(Component)
        +methodA()
        +methodB()
    }

UML

  • Decorated class can be interface, abstract, or a regular class
  • Inheritance is used, but only for type matching if its an abstract or regular class
  • New behavior is gained through composition, not inheritance.
  • New behavior can be added before, or after in decorated objects methods.

Usage

  • Useful when you need to add functionality to another class or object but you can’t, or don’t want to, modify the code of the existing object.
  • When you need an object where functionality needs to be added or removed whimsically (licensed versus unlicensed features, perhaps!).
  • To avoid subclass explosion if a large number of independent features need to be supported and sub-classing to support every combination would lead to class explosion.

Real World Usages

  • java.io package uses decorators to decorate streams of data. Book example, ZipInputStream(BufferedInputStream(FileInputStream))
    • FileInputStream, StringBufferInputStream, ByteArrayInputStream are the concrete components of the Inputstream interface.
    • ZipInputStream, BufferedInputStream are the concrete decorators of the FilterInputStream, which is the component decorator.
  • Adding authentication and authorization to a method or object is a great use for a decorator.
  • Adding encryption and decryption to existing code
  • Adding logging/auditing to existing code
  • Adding a testing framework to existing code for pre and post conditions.
  • Map/Reduce decorators to map and reduce a stream of input for data analytics.

Application to my Real World Application

Java Code Example

Decorator Design Pattern

Main Take-Aways from the textbook

  • Inheritance is usually NOT the best way to extend design.
  • Build code that can be expanded, but without modifying existing code.
  • Composition and delegation is a great way to add new runtime behavior.
  • Avoid subclass explosion with the Decorator Pattern when you have many new features you need to add.
  • Because decorators implement or inherit the base type, you can wrap as many as you want around each-other.
  • Overuse of decorators can make code more cumbersome.

Main OOP Principles of Observer Pattern