Developed By
Gautam Kumar - Full stack developer
DEEP DIVE INTO
The Decorator Pattern is a structural design pattern that allows you to add new behaviors to objects dynamically without altering their existing code.
It is a way to extend the functionalities of objects by composing them with decorators, which are additional classes that wrap or "decorate" the original objects. The Decorator Pattern is commonly used when you need to add responsibilities to objects in a flexible and reusable manner.
Let's take a deep dive into implementing the Decorator Pattern in TypeScript:
Component:
The Component is an abstract class or interface that defines the interface for objects that can be decorated.
It declares the methods that the concrete components and decorators will implement.
Concrete Component:
The Concrete Component is a class that implements the Component interface.
It is the base object to which decorators can be added.
Decorator:
The Decorator is an abstract class or interface that extends the Component interface.
It holds a reference to a Component and defines an interface for all concrete decorators.
Concrete Decorator:
The Concrete Decorator is a class that extends a Decorator and adds specific behavior to the component.
It can modify the behavior of the component it decorates.
Here's an example of how to implement the Decorator Pattern in TypeScript:
javascript// Component (abstract class or interface)
interface Coffee {
cost(): number;
}
// Concrete Component (base coffee)
class SimpleCoffee implements Coffee {
cost(): number {
return 5;
}
}
// Decorator (abstract class)
abstract class CoffeeDecorator implements Coffee {
protected decoratedCoffee: Coffee;
constructor(coffee: Coffee) {
this.decoratedCoffee = coffee;
}
cost(): number {
return this.decoratedCoffee.cost();
}
}
// Concrete Decorators
class MilkDecorator extends CoffeeDecorator {
cost(): number {
return super.cost() + 2;
}
}
class SugarDecorator extends CoffeeDecorator {
cost(): number {
return super.cost() + 1;
}
}
// Client Code
const coffee = new SimpleCoffee();
console.log("Cost of Simple Coffee: $" + coffee.cost());
const coffeeWithMilk = new MilkDecorator(coffee);
console.log("Cost of Coffee with Milk: $" + coffeeWithMilk.cost());
const coffeeWithSugar = new SugarDecorator(coffee);
console.log("Cost of Coffee with Sugar: $" + coffeeWithSugar.cost());
const coffeeWithMilkAndSugar = new SugarDecorator(coffeeWithMilk);
console.log("Cost of Coffee with Milk and Sugar: $" + coffeeWithMilkAndSugar.cost());
In this Decorator Pattern implementation:
Coffee
is the component interface that defines the cost method.
SimpleCoffee
is a concrete component that implements the Coffee interface.
CoffeeDecorator
is the abstract decorator class that holds a reference to a Coffee object and delegates the cost method to it.
MilkDecorator
and SugarDecorator
are concrete decorators that add specific behavior to the component.
The client code can create a base coffee, then decorate it with various decorators to modify its cost and behavior.
Allows you to add and remove responsibilities dynamically.
Avoids altering existing code, making it open for extension and closed for modification (Open/Closed Principle).
Enables you to create a flexible and reusable combination of behaviors.
The Decorator Pattern is widely used for extending and customizing objects in a flexible and maintainable way. It's prevalent in user interfaces for adding features to graphical elements, as well as in input/output stream classes for adding functionalities such as buffering, compression, or encryption.
”