Decorator and Adapter

OOP · lesson 37 of 43 · 3 min read

Two wrapping patterns: one adds behaviour behind the same interface, the other changes the interface.

Open this lesson in the learning hub

Key points

  • Decorator: wrap an object in something that implements the same interface and adds behaviour around it.
  • Decorators stack, and each layer knows only the layer beneath it. BufferedReader around a FileReader is this.
  • Adapter: wrap an object in something that implements a different interface, so two APIs can meet.
  • Both are composition. You add behaviour at runtime instead of writing one subclass per combination of features.
  • Keep each layer thin and never let it reach inside the object it wraps, or you have rebuilt inheritance the hard way.
  • Deep stacks make stack traces long. Three layers is fine; ten usually means one class was wanted.

Example

public class Main {

    interface Coffee {
        int cents();
        String describe();
    }

    record Espresso() implements Coffee {
        public int cents() { return 200; }
        public String describe() { return "espresso"; }
    }

    // DECORATOR: same interface, wraps another Coffee, adds to both answers.
    record WithExtra(Coffee inner, String name, int extra) implements Coffee {
        public int cents() { return inner.cents() + extra; }
        public String describe() { return inner.describe() + " + " + name; }
    }

    // ADAPTER: the till only speaks Priced, so we adapt Coffee to that interface.
    interface Priced { String line(); }

    static Priced adapt(Coffee coffee) {
        return () -> coffee.describe() + " = " + coffee.cents() + " cents";
    }

    public static void main(String[] args) {
        Coffee order = new WithExtra(new WithExtra(new Espresso(), "milk", 50), "syrup", 30);

        System.out.println(order.describe());
        System.out.println(order.cents() + " cents");

        System.out.println(adapt(order).line());
        System.out.println(adapt(new Espresso()).line());
    }
}

Wrap to add behaviour, wrap to change the interface, and keep every layer thin.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the OOP course, and every lesson in it is listed on the OOP contents page.