How do Adapter, Decorator, Proxy, and Facade differ, and how does InputStreamReader vs BufferedReader illustrate the Adapter/Decorator distinction?
answer
- Adapter changes interface; Decorator keeps it + adds behavior
- Proxy keeps interface but controls access; Facade = new simple API
- InputStreamReader = Adapter (bytes->chars)
- BufferedReader = Decorator (Reader->Reader)
- Chain: new BufferedReader(new InputStreamReader(in, UTF_8))
basics
~20 sAll 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 sThese 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// 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
Knows all four wrap an object and can say Adapter changes the interface while Decorator adds behavior.
Correctly classifies InputStreamReader (Adapter) vs BufferedReader (Decorator) and gives the table-level intent of each pattern.
Disambiguates all four by intent and interface impact, explains the java.io chain order, and recognizes mirror cases (OutputStreamWriter, synchronizedList).
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).