In traditional OOP, the Decorator is used to dynamically add responsibilities to objects. Instead of creating subclasses for every combination of features, we compose them step by step.
Example: Decorating a Coffee β
Imagine a coffee shop where you can order a coffee with milk, sugar, or both.
Instead of creating every possible class (CoffeeWithMilkAndSugar, CoffeeWithSugarAndWhippedCreamβ¦), we use the Decorator pattern.
π The goal is to avoid a class explosion and code duplication.
With Decorator, we donβt need a new class for every combination β we just combine a few decorators dynamically.
The agent
public family_interf agent Coffee {
public double getCost();
public String getDescription();
}
π This is the family contract: every coffee, decorated or not, must provide a cost and a description.
The Abstract Decorator
public abstract agent CoffeeDecorator implements Coffee {
private final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
}
π This class is the base for all decorators.
It forwards calls to the wrapped coffee and lets subclasses add extra behavior.
The Concrete Product
public agent SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 2.0; // Base cost of simple coffee
}
@Override
public String getDescription() {
return "Simple Coffee";
}
}
The Concrete Decorators
public agent CoffeeWithMilk extends CoffeeDecorator {
public CoffeeWithMilk(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public double getCost() {
return super.getCost() + 0.5; // Add cost of milk
}
@Override
public String getDescription() {
return super.getDescription() + ", with Milk";
}
}
public agent CoffeeWithSugar extends CoffeeDecorator {
public CoffeeWithSugar(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public double getCost() {
return super.getCost() + 0.2; // Add cost of sugar
}
@Override
public String getDescription() {
return super.getDescription() + ", with Sugar";
}
}
The Launcher
public worker CoffeeShopMaker {
public static void main(String[] args) {
// Order a simple coffee
Coffee coffee = new SimpleCoffee();
System.out.println("Cost: $" + coffee.getCost());
System.out.println("Description: " + coffee.getDescription());
// Decorate it with Milk
coffee = new CoffeeWithMilk(coffee);
System.out.println("Cost: $" + coffee.getCost());
System.out.println("Description: " + coffee.getDescription());
// Decorate it with Sugar
coffee = new CoffeeWithSugar(coffee);
System.out.println("Cost: $" + coffee.getCost());
System.out.println("Description: " + coffee.getDescription());
}
}
Console Output
Cost: $2.0
Description: Simple Coffee
Cost: $2.5
Description: Simple Coffee, with Milk
Cost: $2.7
Description: Simple Coffee, with Milk, with Sugar
Why Decorator Is Clearer in Clprolf
In traditional OOP, the Decorator often looks confusingly close to a Proxy or an Adapter.
With Clprolf, the roles become explicit:
- All coffees (
SimpleCoffee,CoffeeWithMilk,CoffeeWithSugar) areagent. π They all represent the same real-world object: a coffee. - The abstract decorator (
CoffeeDecorator) is also aagent, ensuring the chain of decorations is still the same object type.
The result: there is only one coffee β multiple instances are just a technical solution to decorate it.
Decorator vs Proxy in Clprolf
| Proxy | Decorator |
|---|---|
| Controls or delays access (technical intent) | Adds responsibilities or features (business intent) |
Example: ProxyImage loads an image lazily |
Example: CoffeeWithMilk adds cost + description |
| Different reason for existence | Same agent, but enriched |
Usually a sibling agent
|
Always an agent of the same family |
π In Clprolf, the intent is visible in the declension.
Conclusion
The Decorator Pattern in Clprolf becomes simpler and more consistent:
- There is always one real object (
agent) being enriched. - The abstract decorator ensures chaining works naturally.
β¨ With Clprolf, the Decorator is no longer a confusing βwrapper.β
It is clearly a way to enrich a simulated real object step by step, without cluttering your code with endless subclasses.
Top comments (0)