Decorator Pattern

📢 This article was translated by gemini-2.5-flash

Decorator Pattern: A Structural Design Pattern

Intent

Dynamically add extra responsibilities to an object. When it comes to extending functionality, the Decorator pattern is more flexible than subclassing.

Structure

装饰器

Here’s the breakdown:

  • Component defines an interface for objects, allowing responsibilities to be dynamically added.
  • ConcreteComponent defines an object to which responsibilities can be added.
  • Decorator holds a reference to a Component object and defines an interface consistent with Component’s.
  • ConcreteDecorator adds responsibilities to the component.

Applicability

The Decorator pattern is useful when:

  • You need to add responsibilities to individual objects dynamically and transparently, without affecting other objects.
  • You need to handle responsibilities that can be undone.
  • Subclassing isn’t a practical way to extend functionality. For instance, if you have many independent extensions, subclassing would lead to an explosion of classes to support every combination. Another case is when the class definition is hidden or just not available for subclassing.

Example 1

A coffee shop lets customers add various condiments to their coffee. The shop calculates the cost based on the added condiments. The types and prices of coffee and condiments offered are shown in the table below.

装饰器模式-例子-图1

We’ll use the Decorator pattern to implement this cost calculation feature, resulting in the following class diagram.

装饰器模式-例子-图2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import java.util.*;

    abstract class Beverage { // Beverage
    String description = "Unknown Beverage";
    public String getDescription() { return description; }
    public abstract int cost();
}

abstract class CondimentDecorator extends Beverage { // Condiment
    Beverage beverage;
}

class Espresso extends Beverage { // Espresso
    private final int ESPRESSO_PRICE = 25;
    public Espresso() { description = "Espresso"; }
    public int cost() { return ESPRESSO_PRICE; }
}

class DarkRoast extends Beverage { // Dark Roast Coffee
    private final int DARKROAST_PRICE = 20;
    public DarkRoast() { description = "DarkRoast"; }
    public int cost() { rcturn DARKROAST PRICE; }
}

class Mocha extends CondimentDecorator { // Mocha
    private final int MOCHA_PRICE = 10;
    public Mocha (Beverage beverage) { this.beverage = beverage; }
    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }
    public int cost() { return MOCHA_PRICE + beverage.cost(); }
}

class Whip extends CondimentDecorator { // Whip
    private final int WHIP_PRICE = 8;
    public Whip (Beverage beverage) { this.beverage = beverage; }
    public String getDescription() {
        return beverage.getDescription() + ", Whip";
    }
    public int cost() { return WHIP_PRICE + beverage.cost(); }
}

public class Coffee {
    public static void main(String args[]) {
        Beverage beverage = new DarkRoast();
        beverage = new Mocha(beverage);
        beverage = new Whip(beverage);
        System.out.println(beverage.getDescription() +" ¥"+ beverage.cost());
    }
}

Example 2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class DecoratorPattern {
    public static void main(String[] args) {
        Person p = new Student("Mike");
        p.Operation();

        System.out.println("\n--------------------");

        p = new DecoratorA(p);
        p.Operation();

        System.out.println("\n--------------------");

        p = new DecoratorB(p);
        p.Operation();

        // No.2
        Person p2 = new DecoratorB(new Student("Jane"));
    }
}

abstract class Person{
    protected String name;

    public abstract void Operation();
}

class Student extends Person{
    public Student(String name){
        this.name = name;
    }

    @Override
    public void Operation(){
        System.out.print(name + " Study");
    }
}

abstract class Decorator extends Person{
    protected Person person;

}

class DecoratorA extends Decorator{
    public DecoratorA(Person person){
        this.person = person;
    }

    @Override
    public void Operation() {
        person.Operation();
        System.out.print(" Play");
    }
}

class DecoratorB extends Decorator{
    public DecoratorB(Person person){
        this.person = person;
    }

    @Override
    public void Operation() {
        person.Operation();
        System.out.print(" Exam");
    }
}