DEV Community

Charles Koffler
Charles Koffler

Posted on • Edited on

Decorator Pattern in Clprolf

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();
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ 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();
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ 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";
    }
}
Enter fullscreen mode Exit fullscreen mode

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";
    }
}
Enter fullscreen mode Exit fullscreen mode

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());
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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) are agent. πŸ‘‰ They all represent the same real-world object: a coffee.
  • The abstract decorator (CoffeeDecorator) is also a agent, 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)