skip to content

How do Adapter, Decorator, Proxy, and Facade differ, and how does InputStreamReader vs BufferedReader illustrate the Adapter/Decorator distinction?

level: seniorimportance: should knowfreq 48%

answer

  1. Adapter changes interface; Decorator keeps it + adds behavior
  2. Proxy keeps interface but controls access; Facade = new simple API
  3. InputStreamReader = Adapter (bytes->chars)
  4. BufferedReader = Decorator (Reader->Reader)
  5. Chain: new BufferedReader(new InputStreamReader(in, UTF_8))

basics

~20 s

All four wrap an object, but for different reasons. Adapter changes the interface; Decorator keeps the interface and adds behavior; Proxy keeps the interface and controls access; Facade hides a subsystem behind a simpler interface. InputStreamReader is an Adapter (bytes to chars); BufferedReader is a Decorator (still a Reader, adds buffering).

solid answer

~50 s

These are all wrapping (structural) patterns, distinguished by intent and what they do to the interface. Adapter converts an incompatible interface into the one the client expects — it changes the interface. Decorator preserves the wrapped object's interface and layers on extra behavior, and decorators can stack. Proxy also preserves the interface but its job is to control access — lazy loading, security, remoting, caching — standing in for the real object. Facade introduces a new, simpler interface that hides the complexity of a whole subsystem. In java.io this is vivid: InputStreamReader takes an InputStream (bytes) and exposes a Reader (chars) — different interface, so it's an Adapter. BufferedReader takes a Reader and returns a Reader with buffering and readLine — same interface plus behavior, so it's a Decorator. You often chain them: new BufferedReader(new InputStreamReader(in, UTF_8)) adapts then decorates.

code

java · 6 lines
java
// Adapt (bytes -> chars), then decorate (add buffering + readLine):
BufferedReader r = new BufferedReader(           // Decorator: Reader -> Reader
        new InputStreamReader(                   // Adapter:   InputStream -> Reader
                socket.getInputStream(),         // adaptee (byte source)
                StandardCharsets.UTF_8));
String line = r.readLine();

go deeper

for a junior

Knows all four wrap an object and can say Adapter changes the interface while Decorator adds behavior.

for a middle

Correctly classifies InputStreamReader (Adapter) vs BufferedReader (Decorator) and gives the table-level intent of each pattern.

for a senior

Disambiguates all four by intent and interface impact, explains the java.io chain order, and recognizes mirror cases (OutputStreamWriter, synchronizedList).

for a principal

Uses the family to reason about API layering and extensibility, anticipates abuse (overusing decorators/proxies), and guides teams on choosing the right wrapper at architectural boundaries.

## The shared shape, the differing intent Adapter, Decorator, Proxy, and Facade are all **structural** patterns that **wrap** another object. Interview confusion comes from the similar mechanics (a wrapper holding a wrapped reference). The discriminator is **intent** and **what happens to the interface**. | Pattern | Interface to client | Core intent | |---|---|---| | **Adapter** | **Different** from adaptee's | Make an incompatible type usable through the expected interface | | **Decorator** | **Same** as wrapped | Add responsibilities/behavior dynamically; stackable | | **Proxy** | **Same** as real subject | Control access (lazy, security, remote, caching) | | **Facade** | **New, simpler** | Hide a whole subsystem behind one convenient entry point | ### Adapter — change the interface Client expects interface T; you have an object of incompatible interface S. The adapter implements T and translates to S. The *shape* changes. ### Decorator — same interface, more behavior The decorator implements the **same** interface as what it wraps and forwards most calls, adding behavior around some of them. Because in/out are the same type, decorators **compose/stack** arbitrarily. ### Proxy — same interface, control access Like a decorator structurally (same interface), but the intent is to **stand in** for the real object and govern when/whether/how it's reached: virtual proxy (lazy init), protection proxy (auth), remote proxy (RPC stub), caching proxy. Java's `java.lang.reflect.Proxy` is the dynamic-proxy machinery. ### Facade — new simplified interface Facade doesn't match any existing interface; it **invents** a small, friendly API over a tangle of subsystem classes, so clients avoid the moving parts. ## The java.io showcase The `java.io` stream library is the canonical teaching ground: ```java BufferedReader r = new BufferedReader( // Decorator: Reader -> Reader (+buffer, readLine) new InputStreamReader( // Adapter: InputStream -> Reader (bytes -> chars) socket.getInputStream(), // adaptee: byte source StandardCharsets.UTF_8)); ``` - **`InputStreamReader`** — input is an `InputStream` (byte API), output is a `Reader` (char API). The **interface changes**, so it is an **Adapter**. (It also performs charset decoding, the translation work an adapter does.) - **`BufferedReader`** — input is a `Reader`, output is a `Reader`. The **interface is unchanged**; it merely adds buffering and `readLine()`. So it is a **Decorator**. This is why the two are routinely chained: first adapt the byte stream into a character stream, then decorate that character stream with buffering. The chain reads inside-out: adapt, then decorate. ## Disambiguating in an interview Ask: *Does the wrapper's interface match what it wraps?* - **No, different interface** -> Adapter. - **Yes, same interface, adds behavior** -> Decorator. - **Yes, same interface, controls access/stands in** -> Proxy. - **No — it's a brand-new simplified API over many classes** -> Facade. ## Practical note On the output side, `OutputStreamWriter` is the mirror Adapter (chars -> bytes), and `BufferedWriter` the mirror Decorator. `Collections.synchronizedList`/`unmodifiableList` are also Decorators (same `List` interface, added behavior). Recognizing the family helps you read and extend the JDK fluently.

  • Decorator and Proxy both keep the same interface — how do you tell them apart?
    By intent. Decorator adds behavior/responsibilities and is meant to stack; Proxy stands in for the real object to control access (lazy loading, security, remoting, caching) and usually wraps exactly one subject for that purpose.
  • Why must InputStreamReader come before BufferedReader in the chain?
    BufferedReader decorates a Reader, so it needs a Reader to wrap. The raw source is an InputStream (bytes), so you first adapt it to a Reader with InputStreamReader, then decorate that Reader with BufferedReader.

saying these in an interview costs you the question

  • Calling BufferedReader an Adapter — it keeps the Reader interface, so it's a Decorator.
  • Calling InputStreamReader a Decorator — it changes bytes to chars, so it's an Adapter.
  • Saying Adapter and Decorator are interchangeable because both wrap — intent and interface differ.
  • Confusing Proxy with Decorator by structure alone; the distinguishing factor is intent (access control vs added behavior).

context