Structural Composition: The Decorator Pattern

The **Decorator Pattern** allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class. It is the architectural foundation for **Middleware**, **Interceptors**, and **Resilient Clients**.

I. Stacking Order Matters

In a decorator stack, the order of wrapping determines the execution flow.

* **Outside-In:** The outermost decorator executes first on the request path and last on the response path.

* **Example:** `Logging(Retry(Caching(Service)))` vs `Caching(Logging(Retry(Service)))`.

* In the first case, retries are logged. In the second, only cache misses are logged.

II. Concrete Example: The Resilient HTTP Client

By composing simple decorators, we can build a production-grade HTTP client with multiple cross-cutting concerns.

```java

interface HttpClient { String get(String url); }

class BaseClient implements HttpClient {

@Override public String get(String url) { /* actual I/O */ return ""; }

}

class LoggingDecorator implements HttpClient {

private final HttpClient inner;

public LoggingDecorator(HttpClient inner) { this.inner = inner; }

@Override public String get(String url) {

System.out.println("Calling: " + url);

return inner.get(url);

}

}

class RetryDecorator implements HttpClient {

private final HttpClient inner;

@Override public String get(String url) {

return retry(() -> inner.get(url)); // Logic to retry on 5xx

}

}

// Composition: Resilient Client

HttpClient client = new LoggingDecorator(new RetryDecorator(new BaseClient()));

```

III. Transparency and the "Identity" Problem

A perfect decorator is **Transparent**: the client should not know it is talking to a wrapper.

* **Challenge:** If your code uses `instanceof` or reflection to check for a specific implementation, decorators will break it.

* **Solution:** Always code to the interface, never the implementation.

IV. Dynamic Proxies (JDK Proxy)

For cross-cutting concerns that apply to many interfaces (e.g., generic metrics), use `java.lang.reflect.Proxy`. This avoids creating a manual decorator class for every interface in your system.

```java

HttpClient proxy = (HttpClient) Proxy.newProxyInstance(

HttpClient.class.getClassLoader(),

new Class<?>[]{HttpClient.class},

(p, method, args) -> {

// Intercept logic here

return method.invoke(realObject, args);

}

);

```

---

**See Also:**

- [Proxy Pattern](ProxyPattern) — For controlling access rather than adding behavior.

- [Design Patterns Overview](DesignPatternsOverview) — Context within structural patterns.

- [Adapter Pattern](AdapterPattern) — For translating interfaces rather than wrapping behavior.