Implement a Decorator (e.g., a coffee priced by add-ons). — Cracked Java
// Object-Oriented Programming · Structural Design Patterns
MidCodingEPAM

Implement a Decorator (e.g., a coffee priced by add-ons).

A Decorator preserves the component's interface and adds responsibility by wrapping. The textbook example is a coffee shop where every add-on (milk, sugar, whipped cream) is a wrapper that contributes to the final price and description.

The full implementation

public interface Beverage {
    String description();
    double cost();
}

public class Espresso implements Beverage {
    public String description() { return "Espresso"; }
    public double cost() { return 1.99; }
}

public abstract class CondimentDecorator implements Beverage {
    protected final Beverage beverage;
    protected CondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
}

public class Milk extends CondimentDecorator {
    public Milk(Beverage b) { super(b); }
    public String description() { return beverage.description() + ", milk"; }
    public double cost() { return beverage.cost() + 0.30; }
}

public class Sugar extends CondimentDecorator {
    public Sugar(Beverage b) { super(b); }
    public String description() { return beverage.description() + ", sugar"; }
    public double cost() { return beverage.cost() + 0.10; }
}

Using it

Beverage order = new Sugar(new Milk(new Milk(new Espresso())));
System.out.println(order.description()); // Espresso, milk, milk, sugar
System.out.println(order.cost());        // 2.69

The composition reads inside-out: the innermost Espresso does the base work; each surrounding wrapper extends the previous result. Reverse the order and the description text changes but the price doesn't — addition is commutative; concatenation is not. That asymmetry is itself a clue to which decorators are order-sensitive.

Why an abstract base class

CondimentDecorator exists solely to hold the wrapped beverage reference so concrete decorators don't repeat the constructor and field. It's optional — pure interfaces work too — but the abstract base is the cleanest way to ensure every decorator follows the wrapping contract.

Where the JDK uses this

The classic example is the I/O hierarchy: new BufferedInputStream(new GZIPInputStream(new FileInputStream(path))). Each wrapper preserves the InputStream interface and adds one capability — buffering, decompression, byte source — exactly the Beverage shape with different verbs.

Mark your status