skip to content

Structural Patterns

Patterns for composing classes and objects into larger structures: Adapter, Decorator, Proxy, Facade, Composite, Bridge and Flyweight. They mostly answer how two things that were not designed for each other can work together.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

page 1 of 2

What problem does the Adapter design pattern (Gang of Four, structural category) solve, and what are its participants?

level: juniorimportance: must knowfreq 72%

answer

  1. Convert interface → what client expects
  2. Target / Client / Adaptee / Adapter
  3. Retrofit glue, not new behavior
  4. Translate shape, data, units, errors
  5. Named for what it produces

basics

~20 s

Adapter converts one type's interface into the interface a caller already expects, so two otherwise incompatible pieces of code can work together. The adapter wraps the existing type and translates each call, without changing either side.

solid answer

~50 s

Adapter is a structural pattern whose intent is: convert the interface of a class into another interface clients expect, so classes that could not otherwise collaborate can. Four participants: the Target (the interface the client is coded against), the Client (calls only the Target), the Adaptee (existing, useful code with the wrong interface — a legacy class, a third-party SDK, a vendor library), and the Adapter (implements Target and translates each Target call into one or more Adaptee calls, converting arguments, return values, units and errors). You reach for it when you cannot or should not change either side: the Adaptee is third-party, legacy, or shared by other callers, and the Client is already written against the Target. It is a retrofit pattern — applied after the fact to reconcile a mismatch. Key benefits: reuse without modification, the Open/Closed principle, and one isolated place to change when the vendor changes.

code

pseudocode · 18 lines
pseudocode
// Target — what the client is coded against
interface Shape { render() }

// Adaptee — existing, useful, wrong interface, cannot be changed
class LegacyRectangle { draw(x1, y1, x2, y2) { ... } }

// Adapter — implements Target, delegates to Adaptee, translates data
class RectangleAsShape implements Shape {
  constructor(legacy, box) { this.legacy = legacy; this.box = box }
  render() {
    this.legacy.draw(this.box.left, this.box.top,
                     this.box.left + this.box.width,
                     this.box.top  + this.box.height)
  }
}

// Client never mentions LegacyRectangle
fun paint(shapes: List<Shape>) { for (s in shapes) s.render() }

go deeper

for a junior

State the intent in one sentence and name the four participants, then give one concrete mismatch (your interface says render(), the library says draw(x,y,w,h)).

for a middle

Add the object-vs-class adapter distinction, note that the Target must be owned by the client side, and mention translating errors and data, not just method names.

for a senior

Frame it as a retrofit pattern, contrast with Bridge/Decorator/Facade/Proxy, and discuss what happens when the adaptee cannot fully satisfy the target contract.

for a principal

Scale it up: adapters as the boundary of a hexagonal architecture / anti-corruption layer, who owns the port, contract tests across multiple adapters, and the cost of a lowest-common-denominator interface.

## The problem Two pieces of code do the *same thing* but *say it differently*. Your reporting code calls `render(shape)`; the charting library you bought exposes `draw(x, y, w, h)`. Both draw rectangles. Neither will change: the library is a compiled third-party artifact, and your calling code is spread across fifty call sites (or is itself a framework you don't own). This mismatch is not a *behavior* problem — it is an *interface* problem. **Interface** here means the set of operations a caller may invoke plus their names, parameters, return values and error behavior. In some languages this is a literal `interface`/`protocol`/`trait`; in others it is just "the shape of calls this object supports". The pattern is language-agnostic — it applies wherever one side expects a shape the other doesn't have. ## The intent (GoF wording) > *Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.* Aliases you'll hear: **Wrapper**, **shim**, **translator**, **glue**. ("Wrapper" is ambiguous — Decorator and Proxy also wrap; see below.) ## The four participants | Participant | Role | |---|---| | **Target** | The interface the client is written against, e.g. `Shape.render()`. Owned by *your* side. | | **Client** | Code that only ever talks to the Target. It must not know the Adaptee exists. | | **Adaptee** | The existing, useful implementation with the *wrong* interface, e.g. `LegacyRectangle.draw(x,y,w,h)`. | | **Adapter** | Implements/satisfies Target, holds or extends an Adaptee, and translates. | The direction matters: the Adapter is named for *what it produces* (the Target), not for what it wraps — so `LegacyRectangleShapeAdapter` or, more idiomatically, `LegacyRectangleAsShape`. ## What "translate" actually covers An adapter is rarely a one-line forward. Real adapters convert: - **Method shape** — one Target call may become several Adaptee calls (`open`, `write`, `flush`, `close`), or several Target calls may collapse into one. - **Data model** — a `Shape` object into four primitive coordinates; a vendor DTO into your domain type. - **Units and encodings** — Celsius→Kelvin, cents→decimal, epoch millis→date-time, UTF-8 bytes→text. - **Error vocabulary** — the Adaptee's `SdkHttpException` into your `PaymentUnavailable`. Leaking the Adaptee's exception types through an adapter defeats the entire point, because the client then depends on the Adaptee after all. - **Lifecycle** — pooled connections, disposal, retries. ## When to use it - You want to use an existing class whose interface doesn't match. - You want a reusable class that must cooperate with unforeseen future classes (a **pluggable adapter** — the class defines a narrow port, and adapters are supplied per collaborator). - You must isolate a volatile external dependency behind a stable, owned interface (see also Hexagonal Architecture's *ports and adapters*, and DDD's *anti-corruption layer*). ## When *not* to use it - You control both sides and neither has shipped → just fix the interface. An adapter added "in case" is speculative indirection. - The mismatch is behavioral, not structural (the library simply does the wrong thing). Adapters translate shape, not semantics you don't have. - You need to add behavior to the same interface → that's Decorator. ## Costs One more type, one more hop, one more thing to test; the mapping code is genuinely where bugs hide (unit and null-semantics mistakes). Against that: the client is testable with a fake Target, and a vendor swap touches exactly one class. ## Everyday examples A text-reader over a byte-stream; a collection view over an array; a logging facade routing to whichever logging backend is present; a database driver presenting a standard query interface over a proprietary wire protocol; and physically, a power-plug or USB-C-to-HDMI adapter — the pattern's namesake.

  • Who should own the Target interface — your code or the vendor's SDK?
    Your code. The whole value of the adapter is that the interface is defined by the *consumer's* needs (dependency inversion). If you copy the vendor's interface verbatim as your Target, you've inverted nothing: swapping vendors will still ripple through every client.
  • Is Adapter a compile-time or run-time pattern?
    Both, depending on form. A class adapter (inheritance) is fixed at compile time; an object adapter (composition) picks its adaptee at run time, so one adapter type can serve many adaptee instances or subclasses, and adapters can be chosen by configuration.
  • Does an adapter ever add behavior?
    Legitimately only what translation requires — buffering, caching a handle, converting errors. If it starts adding features while keeping the same interface, it has become a Decorator; if it adds access control or laziness, a Proxy. Naming it honestly matters for maintenance.

A travel power adapter: the laptop charger (adaptee) has the wrong prongs for the wall socket (target). The adapter doesn't change the charger or rewire the hotel — it sits between them and makes the plug fit. It also converts nothing about the electricity's purpose, only its interface.

saying these in an interview costs you the question

  • Saying Adapter adds new functionality — that is Decorator; Adapter only changes the interface.
  • Claiming Adapter simplifies a complex subsystem — that's Facade; Facade invents a new, simpler interface, Adapter conforms to an interface that already exists.
  • Letting the adaptee's exception types or DTOs escape through the adapter, so clients still depend on the vendor.
  • Believing you must modify the adaptee — the point is that you can't or shouldn't.
  • Calling every wrapper class an adapter regardless of intent.

context

open as a page

What problem does the Bridge design pattern solve, and how does its structure solve it?

level: juniorimportance: must knowfreq 55%

basics

~20 s

Bridge splits a design into two separate hierarchies — an abstraction (what the thing is) and an implementation (how it does its work) — connected by a reference instead of inheritance, so each side can grow independently.

open as a page

What problem does the Composite design pattern solve, and what does it mean for a client to treat an individual object and a group of objects "uniformly"?

level: juniorimportance: must knowfreq 68%

basics

~20 s

Composite arranges objects into part-whole trees. A single item and a group of items implement the same interface, so client code calls the same method on either one and never has to check which it is.

open as a page

What is the Decorator design pattern, and why is it called a flexible alternative to subclassing for adding behavior to an object?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Decorator wraps an object inside another object that implements the same interface. The wrapper adds behavior before or after passing the call to the wrapped object. You add features by composing wrappers at runtime instead of writing subclasses.

open as a page

What problem does the Facade design pattern solve, and what does a facade actually add to a system?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Facade puts one simple, higher-level object in front of a complicated group of classes. Clients call the facade instead of wiring the parts together themselves, so calling code is shorter and depends on fewer internal types.

open as a page

What is the Flyweight design pattern, and what problem is it meant to solve?

level: juniorimportance: must knowfreq 55%

basics

~20 s

Flyweight saves memory when a program needs a huge number of similar objects. Instead of one object per occurrence, identical parts are stored once and shared; the parts that differ per occurrence are passed in by the caller.

open as a page

What is the Proxy design pattern, and what problem does it solve?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Proxy is a stand-in object that implements the same interface as a real object and forwards calls to it. Because callers go through the proxy, it can control access — delay creation, check permissions, cache, or talk over a network — without the caller knowing.

open as a page

What problem do structural design patterns solve, and how do they differ from creational and behavioral patterns?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Structural patterns describe how to compose classes and objects into larger structures, usually by wrapping or nesting them. Creational patterns are about how objects get created; behavioral patterns are about how objects talk to each other and split responsibility.

open as a page

Compare the object adapter (composition/delegation) and class adapter (inheritance) forms of the Adapter pattern. What are the trade-offs, and when is each even possible?

level: middleimportance: must knowfreq 55%

basics

~20 s

A class adapter inherits from the adaptee and the target, translating inside itself. An object adapter holds an adaptee instance as a field and delegates. Composition is more flexible and works in single-inheritance languages, so it's the default.

open as a page

How does the Bridge pattern differ from the Adapter pattern, given that both put an interface between a caller and a varying implementation?

level: middleimportance: must knowfreq 60%

basics

~10 s

Bridge is planned before you write either side, to let two dimensions vary independently. Adapter is added afterwards to make an existing, incompatible interface usable. Same shape on a diagram, opposite intent and timing.

open as a page

Walk through the structure of a Composite hierarchy — the participants, where the recursion lives, and how a container computes an aggregate result such as total size or overall validity.

level: middleimportance: must knowfreq 52%

basics

~20 s

A Component interface declares the operation. A Leaf implements it directly (base case). A Composite holds a list of Components and implements the operation by calling it on each child and combining the results (recursive case). The client only ever sees Component.

open as a page

Decorator and Proxy have almost identical structure — both wrap an object behind the same interface. How do you tell them apart, and how do both differ from Adapter?

level: middleimportance: must knowfreq 58%

basics

~20 s

Decorator adds extra behavior to an object; Proxy controls access to it (lazy creation, remote calls, permission checks, lifecycle). Both keep the interface. Adapter is different again: it changes one interface into another so incompatible code can work together.

open as a page

When several Decorator wrappers are stacked around the same object, why does the order of wrapping change behavior? Give concrete examples where the wrong order is a bug.

level: middleimportance: must knowfreq 52%

basics

~20 s

Each wrapper runs its logic around the call to the next one, so the outermost wrapper sees the call first and the result last. Swapping two wrappers changes what each one observes and what it can prevent, which changes behavior.

open as a page

How does the Facade pattern differ from Adapter, Mediator, and Proxy, given that all four place an object in front of something else?

level: middleimportance: must knowfreq 68%

basics

~20 s

Facade simplifies many classes behind one easy entry point. Adapter converts one interface into a different one a client already expects. Mediator makes peers talk through a hub instead of each other. Proxy keeps the same interface but controls access to one object.

open as a page

How do you decide which fields of an object are intrinsic (shareable) versus extrinsic (per-context) when applying the Flyweight pattern, and what happens if you get the split wrong?

level: middleimportance: must knowfreq 45%

basics

~20 s

Ask: "would this value be the same for every place the object is used?" If yes it is intrinsic and can be shared; if it changes per use (position, owner, timestamp) it is extrinsic and must be passed in. Putting per-use data in the shared object corrupts all users.

open as a page

How does the Proxy pattern differ from the Decorator pattern, given that both wrap an object behind the same interface?

level: middleimportance: must knowfreq 68%

basics

~20 s

Their structures look alike, but the intent differs. Proxy controls access to the object — whether, when, and from where you reach it — and usually creates or owns it. Decorator adds extra behavior to a request that always reaches the wrapped object, and is composed from outside.

open as a page

Adapter, Decorator and Proxy all hold a reference to another object and forward calls to it. How do their intents differ, and how do you decide which one you are actually building?

level: middleimportance: must knowfreq 84%

basics

~20 s

Adapter changes an object's interface so an existing client can use it. Decorator keeps the same interface and adds behaviour, and decorators can be stacked. Proxy also keeps the same interface but controls access — lazily creating, checking permissions, caching or reaching a remote object.

open as a page

In the Composite pattern, should child-management operations such as add(child) and remove(child) be declared on the shared Component interface or only on the container class? Explain the trade-off.

level: seniorimportance: must knowfreq 47%

basics

~20 s

Putting add/remove on the shared interface makes leaves and containers fully interchangeable (transparency) but forces leaves to implement operations that are meaningless for them. Putting them only on the container is type-safe but makes clients check the type again.

open as a page

A facade covers the common cases, but a caller now needs a lower-level capability the facade does not expose. What are your options, and what does each cost?

level: seniorimportance: must knowfreq 58%

basics

~20 s

Either leave the subsystem reachable so that caller uses it directly (flexible, weaker boundary), or add the capability to the facade (keeps the boundary, but the facade grows). A middle option is a deliberate escape hatch that exposes the underlying object.

open as a page

Adapter, Decorator, Proxy and Facade are all called "wrappers". Given a wrapper class you've just found in a codebase, how do you tell which of the four it is?

level: middleimportance: should knowfreq 48%

basics

~10 s

Compare the wrapper's interface to the wrapped thing's. Different interface, same behavior = Adapter. Same interface, extra behavior = Decorator. Same interface, controlled access = Proxy. New simpler interface over many objects = Facade.

open as a page

You inherit a hierarchy of classes like EmailAlert, SmsAlert, PushAlert, EmailReminder, SmsReminder, PushReminder. Walk through refactoring it to the Bridge pattern — how do you pick which axis becomes the abstraction and which becomes the implementor?

level: middleimportance: should knowfreq 40%

basics

~20 s

Spot the two independent axes (notification kind × delivery channel). Keep the domain-facing one — the notification kind — as the abstraction hierarchy, extract the mechanism — the channel — into an interface, and have each notification hold and delegate to a channel.

open as a page

What invariants must a Composite container enforce when children are added, removed, or moved — and what breaks if it maintains parent references or shares nodes between containers?

level: middleimportance: should knowfreq 28%

basics

~20 s

add() must reject cycles (a node must not contain itself or an ancestor), keep parent references consistent by detaching the child from its old parent, and enforce any rules about which children are allowed. Sharing one node under two parents breaks parent pointers and double-counts totals.

open as a page

How does the Flyweight pattern differ from an object pool, a cache, memoization, and Singleton — patterns that also hand out reused instances?

level: middleimportance: should knowfreq 40%

basics

~20 s

All reuse objects, but for different reasons. Flyweight shares immutable value-like objects concurrently to save memory. A pool lends out mutable objects one user at a time and takes them back. A cache stores results to avoid recomputation and may evict them. Singleton just limits a class to one instance.

open as a page

You implement a virtual proxy that creates an expensive object on first use. What correctness pitfalls must you handle?

level: middleimportance: should knowfreq 52%

basics

~20 s

Make the lazy creation thread-safe (two threads can both see "not created yet"), decide what happens if creation fails on first call, and remember the proxy is a different object from the real one — identity checks, equality, and type casts on the concrete class can break.

open as a page

What does the Composite pattern give client code, and what is the central trade-off in deciding where child-management operations such as add, remove and getChild live in the hierarchy?

level: middleimportance: should knowfreq 58%

basics

~20 s

Composite arranges objects into a tree where a container and a single item share one interface, so client code can call the same operation on a leaf or on a whole subtree without checking which it has. The trade-off is whether add/remove sit on the shared interface (uniform but unsafe for leaves) or only on containers (type-safe but forces clients to distinguish).

open as a page

When you write an Adapter and the adaptee cannot fully satisfy the target interface — a missing capability, different error model, or different concurrency model — what are your options, and what makes an adapter "leaky"?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Options: emulate the missing behavior inside the adapter, expose a capability check so callers can ask first, narrow the target interface so nobody has to lie, or reject the adaptee. Throwing "unsupported" silently is the worst choice.

open as a page

How does the Adapter pattern differ from the Bridge pattern? Both put an object between two types — what actually separates them?

level: seniorimportance: should knowfreq 42%

basics

~10 s

Adapter is applied after the fact to make two existing, mismatched interfaces work together. Bridge is designed up front so an abstraction and its implementation can vary independently. Same shape, opposite timing and intent.

open as a page

Bridge and Strategy both have an object delegating to an injected interface. What actually distinguishes them?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Strategy swaps one interchangeable algorithm for a single operation and is often changed per call. Bridge splits an entire abstraction from an entire implementation platform, usually fixed for the object's life, to stop a two-dimensional class explosion.

open as a page

Composite, Decorator, and Visitor all involve an object that implements the same interface it holds a reference to, or that walks an object tree. How do you tell Composite apart from Decorator, and when would you combine Composite with Visitor?

level: seniorimportance: should knowfreq 41%

basics

~20 s

Composite holds many children to represent a whole made of parts; Decorator wraps exactly one object to add behaviour to it. Visitor is not a structure at all — it adds new operations over an existing Composite tree without editing the node classes.

open as a page

What practical problems appear once a system leans heavily on Decorator — around object identity, type checks, and calls an object makes to itself?

level: seniorimportance: should knowfreq 34%

basics

~20 s

The wrapper is a different object than the one wrapped, so reference comparison, type checks, and casts to the concrete class fail. Also, when the inner object calls its own methods, those calls skip the wrappers entirely.

open as a page

showing 1–30 of 47