Decorator
The Decorator pattern enables us to change the behavior of an object by hiding the contained functions and adding new functionalities around it. It is also a common way to avoid the limits of single inheritance by multiple interface implementations (composition over inheritance).
Usage
This pattern finds its typical use in framework development because it enables the layering of functionalities around a core service.
Examples
- Caching in HTTP services can be implemented as a decorator pattern by wrapping the http client within a client that analyzes the requested url and prepares a response
- Logging and instrumenting methods can be implmented as a decorator that covers the interactions once for all the possible activities.
Specimens
15 implementationsThe Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class to add behavior, we wrap the object in decorator classes that add the desired functionality. This implementation uses Dart’s type system and composition to achieve this. The Component interface defines the core functionality, ConcreteComponent provides a basic implementation, and Decorator extends Component and holds a reference to another Component. Specific behaviors are added by ConcreteDecorator classes, which wrap the component and augment its behavior. This approach is idiomatic Dart as it favors composition over inheritance and leverages Dart’s flexible type system.
// Component interface
abstract class Component {
String operation();
}
// Concrete Component
class ConcreteComponent implements Component {
@override
String operation() => 'Base operation';
}
// Decorator abstract class
abstract class Decorator implements Component {
final Component _component;
Decorator(this._component);
@override
String operation() => _component.operation();
}
// Concrete Decorator 1
class ConcreteDecoratorA extends Decorator {
ConcreteDecoratorA(Component component) : super(component);
@override
String operation() {
return 'Decorator A: ' + super.operation();
}
}
// Concrete Decorator 2
class ConcreteDecoratorB extends Decorator {
ConcreteDecoratorB(Component component) : super(component);
@override
String operation() {
return 'Decorator B: ' + super.operation();
}
}
void main() {
Component component = ConcreteComponent();
print(component.operation());
Component decoratorA = ConcreteDecoratorA(component);
print(decoratorA.operation());
Component decoratorB = ConcreteDecoratorB(decoratorA);
print(decoratorB.operation());
}
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Here, we define a base Beverage trait and concrete implementations like Espresso. Decorators, also traits, wrap a Beverage and add cost and description. MilkDecorator and SoyDecorator are examples. The main method demonstrates how to compose decorators to create a beverage with varying additions. This implementation is idiomatic Scala due to its use of traits for defining types and composition over inheritance, aligning with Scala’s functional and object-oriented strengths.
trait Beverage {
val cost: Double
val description: String
}
class Espresso extends Beverage {
val cost = 2.0
val description = "Espresso"
}
trait BeverageDecorator extends Beverage {
protected var decoratedBeverage: Beverage
def this(beverage: Beverage) = {
this.decoratedBeverage = beverage
this
}
}
class MilkDecorator(beverage: Beverage) extends BeverageDecorator {
val cost = beverage.cost + 0.5
val description = beverage.description + ", Milk"
}
class SoyDecorator(beverage: Beverage) extends BeverageDecorator {
val cost = beverage.cost + 0.7
val description = beverage.description + ", Soy"
}
object Main {
def main(args: Array[String]): Unit = {
val espresso = new Espresso()
val milkEspresso = new MilkDecorator(espresso)
val soyMilkEspresso = new SoyDecorator(milkEspresso)
println(s"Description: ${soyMilkEspresso.description}")
println(s"Cost: $${soyMilkEspresso.cost}")
}
}
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class to add features, we wrap the original object with one or more decorator objects, each adding a specific responsibility. This implementation uses interfaces to define the core component and the decorator, allowing for multiple decorators to be stacked. PHP’s dynamic nature and lack of true multiple inheritance make the Decorator pattern particularly useful for extending object behavior without rigid class hierarchies, fitting its flexible design principles.
<?php
/**
* Component Interface: Defines the interface for objects that can have
* responsibilities added dynamically.
*/
interface Coffee {
public function getCost(): float;
public function getDescription(): string;
}
/**
* Concrete Component: The base coffee object.
*/
class SimpleCoffee implements Coffee {
public function getCost(): float {
return 2.0;
}
public function getDescription(): string {
return "Simple coffee";
}
}
/**
* Decorator Abstract Class: Holds a reference to the object it decorates.
*/
abstract class CoffeeDecorator implements Coffee {
protected Coffee $coffee;
public function __construct(Coffee $coffee) {
$this->coffee = $coffee;
}
}
/**
* Concrete Decorator: Adds milk to the coffee.
*/
class MilkDecorator extends CoffeeDecorator {
public function getCost(): float {
return $this->coffee->getCost() + 0.5;
}
public function getDescription(): string {
return $this->coffee->getDescription() . ", with milk";
}
}
/**
* Concrete Decorator: Adds sugar to the coffee.
*/
class SugarDecorator extends CoffeeDecorator {
public function getCost(): float {
return $this->coffee->getCost() + 0.2;
}
public function getDescription(): string {
return $this->coffee->getDescription() . ", with sugar";
}
}
// Usage
$coffee = new SimpleCoffee();
$coffee = new MilkDecorator($coffee);
$coffee = new SugarDecorator($coffee);
echo "Description: " . $coffee->getDescription() . "\n";
echo "Cost: $" . $coffee->getCost() . "\n";
?>
The Decorator pattern dynamically adds responsibilities to an object without modifying its structure. It provides a flexible alternative to subclassing for extending functionality. This implementation uses Ruby’s dynamic nature and block-based approach to define decorators as classes that wrap the original object and add behavior. The decorate method allows for chaining decorators, enhancing the object with multiple responsibilities. This approach is idiomatic Ruby as it favors composition over inheritance and leverages blocks for concise, flexible code.
# Component interface
class Coffee
def cost
raise NotImplementedError
end
def description
"Simple Coffee"
end
end
# Concrete Component
class SimpleCoffee < Coffee
def cost
1.0
end
end
# Decorator abstract class
class CoffeeDecorator < Coffee
def initialize(coffee)
@coffee = coffee
end
def cost
@coffee.cost
end
def description
@coffee.description
end
end
# Concrete Decorators
class MilkDecorator < CoffeeDecorator
def cost
@coffee.cost + 0.5
end
def description
@coffee.description + ", with Milk"
end
end
class SugarDecorator < CoffeeDecorator
def cost
@coffee.cost + 0.2
end
def description
@coffee.description + ", with Sugar"
end
end
# Usage
coffee = SimpleCoffee.new
puts "Cost: #{coffee.cost}, Description: #{coffee.description}"
decorated_coffee = MilkDecorator.new(coffee)
puts "Cost: #{decorated_coffee.cost}, Description: #{decorated_coffee.description}"
more_decorated_coffee = SugarDecorator.new(decorated_coffee)
puts "Cost: #{more_decorated_coffee.cost}, Description: #{more_decorated_coffee.description}"
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class to add features, we wrap the original object with one or more decorator objects, each adding a specific behavior.
This Swift implementation uses protocol-oriented programming, which is highly idiomatic. The Coffee protocol defines the core functionality. BaseCoffee conforms to this protocol, providing a basic implementation. Decorator also conforms to Coffee, holding a Coffee instance and delegating to it. Concrete decorators like MilkDecorator and SugarDecorator add their specific behaviors before delegating to the wrapped Coffee. This avoids modifying the original Coffee class and allows for runtime composition of features.
protocol Coffee {
var cost: Double { get }
var description: String { get }
}
class BaseCoffee: Coffee {
let description: String
let cost: Double
init(description: String, cost: Double) {
self.description = description
self.cost = cost
}
}
class CoffeeDecorator: Coffee {
let coffee: Coffee
init(coffee: Coffee) {
self.coffee = coffee
}
var cost: Double {
return coffee.cost
}
var description: String {
return coffee.description
}
}
class MilkDecorator: CoffeeDecorator {
override var cost: Double {
return super.cost + 0.5
}
override var description: String {
return super.description + ", with milk"
}
}
class SugarDecorator: CoffeeDecorator {
override var cost: Double {
return super.cost + 0.2
}
override var description: String {
return super.description + ", with sugar"
}
}
// Example Usage
let simpleCoffee = BaseCoffee(description: "Simple coffee", cost: 2.0)
let milkCoffee = MilkDecorator(coffee: simpleCoffee)
let sugaryMilkCoffee = SugarDecorator(coffee: milkCoffee)
print(sugaryMilkCoffee.description) // Output: Simple coffee, with milk, with sugar
print(sugaryMilkCoffee.cost) // Output: 2.7
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class to add behavior, you “wrap” the original object with decorator classes, each adding a specific responsibility. This implementation uses Kotlin’s extension functions and interfaces to achieve a clean and concise decorator. The Coffee interface defines the core behavior, while concrete coffee types implement it. CoffeeDecorator is an abstract class that holds a reference to a Coffee and delegates to it, allowing decorators to add functionality before or after the core coffee behavior. This approach leverages Kotlin’s functional aspects and avoids the verbosity of traditional Java Decorator implementations.
// Component Interface
interface Coffee {
fun brew(): String
fun getCost(): Double
}
// Concrete Component
class SimpleCoffee : Coffee {
override fun brew(): String = "Simple coffee"
override fun getCost(): Double = 1.0
}
// Decorator
abstract class CoffeeDecorator(protected var coffee: Coffee) : Coffee {
override fun brew(): String {
return coffee.brew()
}
override fun getCost(): Double {
return coffee.getCost()
}
}
// Concrete Decorators
class MilkDecorator(coffee: Coffee) : CoffeeDecorator(coffee) {
override fun brew(): String = "${coffee.brew()} with milk"
override fun getCost(): Double = coffee.getCost() + 0.5
}
class SugarDecorator(coffee: Coffee) : CoffeeDecorator(coffee) {
override fun brew(): String = "${coffee.brew()} with sugar"
override fun getCost(): Double = coffee.getCost() + 0.2
}
fun main() {
val coffee = SimpleCoffee()
println(coffee.brew()) // Output: Simple coffee
println("Cost: $${coffee.getCost()}") // Output: Cost: $1.0
val milkCoffee = MilkDecorator(coffee)
println(milkCoffee.brew()) // Output: Simple coffee with milk
println("Cost: $${milkCoffee.getCost()}") // Output: Cost: $1.5
val sugarMilkCoffee = SugarDecorator(milkCoffee)
println(sugarMilkCoffee.brew()) // Output: Simple coffee with milk with sugar
println("Cost: $${sugarMilkCoffee.getCost()}") // Output: Cost: $1.7
}
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. This implementation uses Rust’s trait objects and the Box type to achieve this. We define a Report trait with a display method. The SimpleReport is the concrete component. Decorator is an abstract decorator that holds a reference to the report it decorates and delegates to it. DetailedReport adds extra information by wrapping a Report and extending its display functionality. This approach is idiomatic Rust because it leverages traits for polymorphism and Box for dynamic dispatch, avoiding concrete inheritance hierarchies.
// Define the component interface
trait Report {
fn display(&self) -> String;
}
// Concrete component
struct SimpleReport {
content: String,
}
impl SimpleReport {
fn new(content: String) -> Self {
SimpleReport { content }
}
}
impl Report for SimpleReport {
fn display(&self) -> String {
self.content.clone()
}
}
// The Decorator abstract class
trait Decorator: Report {
fn get_component(&self) -> &dyn Report;
}
struct ConcreteDecorator<'a> {
component: Box<dyn Report>,
additional_info: String,
}
impl<'a> ConcreteDecorator<'a> {
fn new(component: Box<dyn Report>, additional_info: String) -> Self {
ConcreteDecorator { component, additional_info }
}
}
impl<'a> Report for ConcreteDecorator<'a> {
fn display(&self) -> String {
self.component.display() + &self.additional_info
}
}
// Concrete Decorator
struct DetailedReport<'a> {
report: Box<dyn Report>,
}
impl<'a> DetailedReport<'a> {
fn new(report: Box<dyn Report>) -> Self {
DetailedReport { report }
}
}
impl<'a> Report for DetailedReport<'a> {
fn display(&self) -> String {
format!("Detailed Report:\n{}", self.report.display())
}
}
fn main() {
let simple_report = Box::new(SimpleReport::new("This is the base report.".to_string()));
let detailed_report = DetailedReport::new(simple_report);
println!("{}", detailed_report.display());
let decorated_report = ConcreteDecorator::new(Box::new(SimpleReport::new("Another report.".to_string())), " - with extra details!".to_string());
println!("{}", decorated_report.display());
}
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class, objects are wrapped in decorator objects that add behavior before, after, or around the original object’s methods.
This Go implementation uses interfaces to define the core component and decorators. The Service interface represents the base component, and concrete services implement it. Decorator is an interface that all decorators implement, containing the decorated Service and a method to execute the service. The ConcreteDecorator type adds specific functionality before and after calling the underlying service, demonstrating dynamic extension without modifying the original Service implementation. This approach is idiomatic Go because it leverages interfaces and composition over inheritance.
// service.go
package main
import "fmt"
// Service interface defines the core functionality.
type Service interface {
Do()
}
// ConcreteService is a concrete implementation of the Service interface.
type ConcreteService struct{}
func (s *ConcreteService) Do() {
fmt.Println("ConcreteService: Doing something.")
}
// Decorator interface defines the decorator's structure.
type Decorator interface {
Service
}
// ConcreteDecorator adds functionality to the Service.
type ConcreteDecorator struct {
decoratedService Service
}
func NewConcreteDecorator(s Service) *ConcreteDecorator {
return &ConcreteDecorator{decoratedService: s}
}
func (d *ConcreteDecorator) Do() {
fmt.Println("ConcreteDecorator: Before service.")
d.decoratedService.Do()
fmt.Println("ConcreteDecorator: After service.")
}
// AnotherDecorator adds different functionality.
type AnotherDecorator struct {
decoratedService Service
}
func NewAnotherDecorator(s Service) *AnotherDecorator {
return &AnotherDecorator{decoratedService: s}
}
func (a *AnotherDecorator) Do() {
fmt.Println("AnotherDecorator: Adding extra behavior.")
a.decoratedService.Do()
}
func main() {
service := &ConcreteService{}
// Decorate the service with ConcreteDecorator
decoratedService := NewConcreteDecorator(service)
decoratedService.Do()
// Further decorate with AnotherDecorator
anotherDecoratedService := NewAnotherDecorator(decoratedService)
anotherDecoratedService.Do()
}
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Here, we define a base Coffee interface and concrete implementations like SimpleCoffee. Decorators, like MilkDecorator and SugarDecorator, wrap a Coffee object and add their own behavior (milk or sugar) without altering the original Coffee’s class. The decorate() method recursively adds decorators, building up the desired functionality. This C implementation uses function pointers to achieve the dynamic behavior, a common approach in C for simulating polymorphism and achieving flexibility similar to interfaces in other languages.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Component Interface
typedef struct Coffee Coffee;
typedef void (*CoffeeFunc)(Coffee*);
struct Coffee {
CoffeeFunc drink;
char* description;
};
void simpleCoffeeDrink(Coffee* coffee) {
printf("Drinking a simple coffee: %s\n", coffee->description);
}
// Concrete Component
Coffee* createSimpleCoffee() {
Coffee* coffee = (Coffee*)malloc(sizeof(Coffee));
if (coffee == NULL) {
perror("Failed to allocate memory for coffee");
exit(EXIT_FAILURE);
}
coffee->drink = simpleCoffeeDrink;
coffee->description = strdup("Simple Coffee");
return coffee;
}
// Decorator
typedef struct CoffeeDecorator CoffeeDecorator;
struct CoffeeDecorator {
Coffee* coffee;
char* description;
};
CoffeeDecorator* createCoffeeDecorator(Coffee* coffee) {
CoffeeDecorator* decorator = (CoffeeDecorator*)malloc(sizeof(CoffeeDecorator));
if (decorator == NULL) {
perror("Failed to allocate memory for decorator");
exit(EXIT_FAILURE);
}
decorator->coffee = coffee;
decorator->description = strdup("Decorated Coffee");
return decorator;
}
// Concrete Decorators
typedef struct MilkDecorator MilkDecorator;
typedef struct SugarDecorator SugarDecorator;
struct MilkDecorator {
CoffeeDecorator base;
char* milk_description;
};
struct SugarDecorator {
CoffeeDecorator base;
char* sugar_description;
};
void milkDecoratorDrink(CoffeeDecorator* decorator) {
printf("Adding milk: %s\n", decorator->base.coffee->description);
printf("Drinking a coffee with milk: %s\n", decorator->milk_description);
}
void sugarDecoratorDrink(CoffeeDecorator* decorator) {
printf("Adding sugar: %s\n", decorator->base.coffee->description);
printf("Drinking a coffee with sugar: %s\n", decorator->sugar_description);
}
CoffeeDecorator* createMilkDecorator(Coffee* coffee) {
CoffeeDecorator* decorator = createCoffeeDecorator(coffee);
MilkDecorator* milkDecorator = (MilkDecorator*)decorator;
milkDecorator->milk_description = strdup("Milk Coffee");
milkDecorator->base.coffee->drink(milkDecorator); // Call the underlying coffee's drink method
return decorator;
}
CoffeeDecorator* createSugarDecorator(Coffee* coffee) {
CoffeeDecorator* decorator = createCoffeeDecorator(coffee);
SugarDecorator* sugarDecorator = (SugarDecorator*)decorator;
sugarDecorator->sugar_description = strdup("Sugar Coffee");
sugarDecorator->base.coffee->drink(sugarDecorator); // Call the underlying coffee's drink method
return decorator;
}
int main() {
Coffee* simple = createSimpleCoffee();
CoffeeDecorator* milk = createMilkDecorator(simple);
CoffeeDecorator* sugar = createSugarDecorator(milk);
// Cleanup (important in C)
free(simple->description);
free(simple);
free(milk->milk_description);
free(milk);
free(sugar->sugar_description);
free(sugar);
return 0;
}
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class to add behavior, we wrap the object with decorator classes that implement the same interface. This allows for combining behaviors at runtime without altering the original object’s class.
The C++ example demonstrates this by having a Component interface (abstract class) representing the object to be decorated. ConcreteComponent is the base object. Decorator is an abstract base class for decorators, holding a pointer to the component. ConcreteDecoratorA and ConcreteDecoratorB add specific responsibilities by overriding the operation method, first calling the component’s operation and then adding their own. This approach is idiomatic C++ due to its use of polymorphism and pointers to achieve runtime flexibility.
#include <iostream>
#include <string>
// Component interface
class Component {
public:
virtual std::string operation() = 0;
};
// Concrete Component
class ConcreteComponent : public Component {
public:
std::string operation() override {
return "Core functionality";
}
};
// Decorator base class
class Decorator : public Component {
public:
Decorator(Component* component) : component_(component) {}
std::string operation() override {
return component_->operation();
}
protected:
Component* component_;
};
// Concrete Decorator A
class ConcreteDecoratorA : public Decorator {
public:
ConcreteDecoratorA(Component* component) : Decorator(component) {}
std::string operation() override {
return Decorator::operation() + " with added feature A";
}
};
// Concrete Decorator B
class ConcreteDecoratorB : public Decorator {
public:
ConcreteDecoratorB(Component* component) : Decorator(component) {}
std::string operation() override {
return Decorator::operation() + " and feature B";
}
};
int main() {
Component* simple = new ConcreteComponent();
std::cout << "Simple component: " << simple->operation() << std::endl;
Component* decorated = new ConcreteDecoratorA(simple);
std::cout << "Decorated with A: " << decorated->operation() << std::endl;
Component* doubly_decorated = new ConcreteDecoratorB(decorated);
std::cout << "Decorated with A and B: " << doubly_decorated->operation() << std::endl;
delete simple;
delete decorated;
delete doubly_decorated;
return 0;
}
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class to add behavior, you “wrap” the original object with decorator classes, each adding a specific responsibility. This implementation uses interfaces to define the core component and the decorator, allowing for multiple decorators to be stacked. C#’s composition-based approach makes the Decorator pattern a natural fit, avoiding the rigidity of inheritance and promoting loose coupling.
// Component Interface
public interface IMessage
{
string GetMessage();
}
// Concrete Component
public class SimpleMessage : IMessage
{
private readonly string _message;
public SimpleMessage(string message)
{
_message = message;
}
public string GetMessage()
{
return _message;
}
}
// Decorator Abstract Class
public abstract class MessageDecorator : IMessage
{
protected readonly IMessage _message;
protected MessageDecorator(IMessage message)
{
_message = message;
}
public virtual string GetMessage()
{
return _message.GetMessage();
}
}
// Concrete Decorators
public class EncryptionDecorator : MessageDecorator
{
public EncryptionDecorator(IMessage message) : base(message) { }
public override string GetMessage()
{
return "Encrypted: " + base.GetMessage();
}
}
public class PriorityDecorator : MessageDecorator
{
public PriorityDecorator(IMessage message) : base(message) { }
public override string GetMessage()
{
return "Priority: " + base.GetMessage();
}
}
// Example Usage
public class Program
{
public static void Main(string[] args)
{
IMessage message = new SimpleMessage("Hello, World!");
message = new EncryptionDecorator(message);
message = new PriorityDecorator(message);
Console.WriteLine(message.GetMessage()); // Output: Priority: Encrypted: Hello, World!
}
}
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. In this TypeScript example, we define a base Component class and then use decorator functions (@logExecution, @throttle) to add logging and rate-limiting behavior to a specific component instance without modifying its core code. TypeScript’s decorator syntax allows for clean and concise modification of classes and methods. This implementation leverages TypeScript’s type system for safety and readability, aligning with its best practices for code extensibility.
// Component interface
interface Component {
execute(): void;
}
// Base Component class
class BasicComponent implements Component {
public execute(): void {
console.log("BasicComponent executing...");
}
}
// Decorator: Log execution
function logExecution(constructor: any) {
return class extends constructor {
execute() {
console.log("Executing decorated method...");
super.execute();
}
};
}
// Decorator: Throttle execution
function throttle(milliseconds: number) {
return function (constructor: any) {
return class extends constructor {
private timeoutId: NodeJS.Timeout | null = null;
execute() {
if (!this.timeoutId) {
super.execute();
this.timeoutId = setTimeout(() => {
this.timeoutId = null;
}, milliseconds);
}
}
};
};
}
// Apply decorators
@logExecution
@throttle(500)
class EnhancedComponent extends BasicComponent {
execute() {
console.log("EnhancedComponent executing...");
super.execute();
}
}
// Usage
const enhanced = new EnhancedComponent();
enhanced.execute(); // Logs and throttles
enhanced.execute(); // Throttled
enhanced.execute(); // Throttled
setTimeout(() => {
enhanced.execute(); // Executes after throttle
}, 600);
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class, you “wrap” the original object with decorator objects, each adding a specific behavior. This implementation uses closures to encapsulate the decorated functionality, a common JavaScript approach. The decorate function takes a base object and a series of decorator functions, applying them sequentially to enhance the object’s behavior without modifying its core. This leverages JavaScript’s first-class function capabilities and avoids rigid class hierarchies.
/**
* Decorator Pattern Implementation in JavaScript
*/
// Base Component
const coffee = {
cost: () => 5,
description: () => "Simple coffee"
};
// Decorator base
const decorate = (base, ...decorators) => {
return decorators.reduce((acc, decorator) => decorator(acc), base);
};
// Concrete Decorators
const milkDecorator = (coffee) => {
return {
cost: () => coffee.cost() + 2,
description: () => `Coffee with milk - ${coffee.description()}`
};
};
const sugarDecorator = (coffee) => {
return {
cost: () => coffee.cost() + 1,
description: () => `Coffee with sugar - ${coffee.description()}`
};
};
const whippedCreamDecorator = (coffee) => {
return {
cost: () => coffee.cost() + 3,
description: () => `Coffee with whipped cream - ${coffee.description()}`
};
}
// Usage
const myCoffee = decorate(coffee, milkDecorator, sugarDecorator);
const fancyCoffee = decorate(coffee, milkDecorator, whippedCreamDecorator, sugarDecorator);
console.log(myCoffee.description()); // Output: Coffee with milk - Simple coffee
console.log(myCoffee.cost()); // Output: 7
console.log(fancyCoffee.description()); // Output: Coffee with whipped cream - Coffee with milk - Simple coffee
console.log(fancyCoffee.cost()); // Output: 10
The Decorator pattern dynamically adds responsibility to an object without modifying its structure. It provides a flexible alternative to subclassing for extending functionality. In this Python example, we define a base function say_hello and then create decorator functions add_greeting and make_it_loud that wrap the original function, adding new behavior (a greeting prefix and uppercase conversion, respectively). The @decorator_name syntax is Python’s syntactic sugar for applying a decorator, making the code concise and readable. This approach aligns with Python’s emphasis on first-class functions and dynamic programming.
def add_greeting(func):
"""Adds a greeting prefix to the function's output."""
def wrapper(*args, **kwargs):
return "Hello, " + func(*args, **kwargs)
return wrapper
def make_it_loud(func):
"""Converts the function's output to uppercase."""
def wrapper(*args, **kwargs):
return func(*args, **kwargs).upper()
return wrapper
@add_greeting
@make_it_loud
def say_hello(name):
"""A simple function that returns a greeting."""
return f"hello {name}"
if __name__ == "__main__":
print(say_hello("world")) # Output: HELLO, WORLD
The Decorator pattern dynamically adds responsibilities to an object. It provides a flexible alternative to subclassing for extending functionality. Instead of inheriting from a base class to add behavior, you “wrap” the original object with decorator classes, each adding a specific responsibility. This implementation uses Java interfaces to define the core component and the decorator. Concrete components and decorators then implement these interfaces. The decorate() method allows for stacking decorators, enhancing the object’s behavior incrementally. This approach aligns with Java’s preference for composition over inheritance and promotes loose coupling.
// Component Interface
interface Coffee {
String getDescription();
double getCost();
}
// Concrete Component
class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple coffee";
}
@Override
public double getCost() {
return 2.0;
}
}
// Decorator Abstract Class
abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return coffee.getDescription();
}
@Override
public double getCost() {
return coffee.getCost();
}
}
// Concrete Decorators
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", with milk";
}
@Override
public double getCost() {
return coffee.getCost() + 0.5;
}
}
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", with sugar";
}
@Override
public double getCost() {
return coffee.getCost() + 0.2;
}
}
// Example Usage
public class DecoratorExample {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
System.out.println("Cost: $" + coffee.getCost() + ", Description: " + coffee.getDescription());
coffee = new MilkDecorator(coffee);
System.out.println("Cost: $" + coffee.getCost() + ", Description: " + coffee.getDescription());
coffee = new SugarDecorator(coffee);
System.out.println("Cost: $" + coffee.getCost() + ", Description: " + coffee.getDescription());
}
}