Skip to content

Latest commit

 

History

History
102 lines (83 loc) · 8.4 KB

Decorator pattern.md

File metadata and controls

102 lines (83 loc) · 8.4 KB

Decorator pattern

The Decorator Design Pattern is a structural design pattern that allows behavior to be added to individual objects dynamically, without affecting the behavior of other objects from the same class. It involves creating a set of decorator classes that are used to wrap concrete components.1

The decorator pattern provides a flexible alternative to subclassing for extending functionality. When using subclassing, different subclasses extend a class in different ways. However, an extension is bound to the class at compile-time and can't be changed at run-time. The decorator pattern allows responsibilities to be added (and removed from) an object dynamically at run-time. It is achieved by defining Decorator objects that:

  • implement the interface of the extended (decorated) object (Component) transparently by forwarding all requests to it;
  • perform additional functionality before or after forwarding a request.

This allows working with different Decorator objects to extend the functionality of an object dynamically at run-time.2

The Decorator Pattern works best when you need to add features to objects without changing their core structure. Use it for:3

  • Adding or removing behaviors at runtime;
  • Enhancing legacy systems without touching their code;
  • Avoiding a explosion of subclasses.

Don't use it if:4

  • Objects' core functionality changes often;
  • You need to modify object internals;
  • You already have many small, similar classes.

Implementation steps:5

  • Define an interface for the main object;
  • Create concrete components implementing the main object interface;
  • Create abstract decorator classes that also implement the main object interface;
  • Implement concrete decorators by inheriting from the abstract decorator.
// Main object interface
interface Coffee {
    fun cost(): Double
    fun description(): String
}
// Concrete component
class BasicCoffee : Coffee {
    override fun cost() = 2.0
    override fun description() = "Basic Coffee"
}
// Abstract decorator
abstract class CoffeeDecorator(private val coffee: Coffee) : Coffee {
    override fun cost() = coffee.cost()
    override fun description() = coffee.description()
}
// Concrete decorators
class MilkDecorator(coffee: Coffee) : CoffeeDecorator(coffee) {
    override fun cost() = super.cost() + 0.5
    override fun description() = super.description() + ", Milk"
}
class SugarDecorator(coffee: Coffee) : CoffeeDecorator(coffee) {
    override fun cost() = super.cost() + 0.2
    override fun description() = super.description() + ", Sugar"
}
fun main() {
    val coffee: Coffee = SugarDecorator(MilkDecorator(BasicCoffee()))
    println("${coffee.description()} costs \$${coffee.cost()}")
}

Output:

Basic Coffee, Milk, Sugar costs $2.7

Explanation:6

  • Coffee is the main object interface, defining methods to get the cost and description;
  • BasicCoffee is a concrete component that implements the Coffee interface;
  • CoffeeDecorator is an abstract decorator that implements the Coffee interface and wraps another Coffee object;
  • MilkDecorator and SugarDecorator are concrete decorators that add their respective features to the coffee;
  • In the main function, a coffee with milk and sugar is created using decorators. The final cost and description are calculated based on all layers of decorators.
  • Open-Closed Principle. The decorator pattern follows the open-closed principle, which states that classes should be open for extension but closed for modification. This means you can introduce new functionality to an existing class without changing its source code;
  • Flexibility. It allows you to add or remove responsibilities (i.e., behaviors) from objects at runtime. This flexibility makes it easy to create complex object structures with varying combinations of behaviors;
  • Reusable Code. Decorators are reusable components. You can create a library of decorator classes and apply them to different objects and classes as needed, reducing code duplication;
  • Composition over Inheritance. Unlike traditional inheritance, which can lead to a deep and inflexible class hierarchy, the decorator pattern uses composition. You can compose objects with different decorators to achieve the desired functionality, avoiding the drawbacks of inheritance, such as tight coupling and rigid hierarchies;
  • Dynamic Behavior Modification. Decorators can be applied or removed at runtime, providing dynamic behavior modification for objects. This is particularly useful when you need to adapt an object’s behavior based on changing requirements or user preferences;
  • Clear Code Structure. The Decorator pattern promotes a clear and structured design, making it easier for developers to understand how different features and responsibilities are added to objects.
  • Complexity. As you add more decorators to an object, the code can become more complex and harder to understand. The nesting of decorators can make the codebase difficult to navigate and debug, especially when there are many decorators involved;
  • Increased Number of Classes. When using the Decorator pattern, you often end up with a large number of small, specialized decorator classes. This can lead to a proliferation of classes in your codebase, which may increase maintenance overhead;
  • Order of Decoration. The order in which decorators are applied can affect the final behavior of the object. If decorators are not applied in the correct order, it can lead to unexpected results. Managing the order of decorators can be challenging, especially in complex scenarios;
  • Potential for Overuse. Because it’s easy to add decorators to objects, there is a risk of overusing the Decorator pattern, making the codebase unnecessarily complex. It’s important to use decorators judiciously and only when they genuinely add value to the design.

Links

Decorator Design Pattern

Decorator pattern

Decorator Pattern Explained: Basics to Advanced

Decorator Design Pattern in Kotlin

Further reading

Decorator Design Pattern

Decorator

Decorator Software Pattern Kotlin Examples

Decorator Pattern in Jetpack Compose Android Apps

Decorator Pattern in Kotlin