Java Omnibus
OOAD & Architecture / GoF patterns / Structural / Adapter vs Facade vs Proxy
GoF Structural Decision Guide Java
Pattern Comparison

These three wrappers look similar on the surface, but they solve three very different structural problems.

Adapter, Facade, and Proxy all wrap something, which is why they are often confused. The clean distinction is the force. Adapter handles interface mismatch. Facade simplifies a noisy subsystem. Proxy controls access to an object while preserving the same interface.

Why This Topic Matters

Why this wrapper choice matters

Java systems wrap external SDKs, internal subsystems, repositories, and remote clients constantly. Choosing the wrong wrapper shape makes code harder to read because the intent becomes unclear.

Core Explanation

How to separate these structural roles

The similarity between these patterns is only that they sit between a client and something else. The real difference is why they exist. Adapter changes the visible interface. Facade creates a simpler entry point into several collaborators. Proxy preserves the interface but changes how access happens.

In Java work, that distinction matters because integrations, service orchestration, security wrappers, and remote calls all produce wrapper classes. If you can name the force clearly, the right pattern is usually obvious.

Choose the pattern that matches the reason for the wrapper, not the fact that a wrapper exists.

Concept Breakdown

The forces behind the wrapper

Force

Shape mismatch

Adapter is for translation when a useful collaborator exposes the wrong interface.

Force

Subsystem simplification

Facade is for giving clients a simpler, higher-level way to use a more complex subsystem.

Force

Access mediation

Proxy is for controlling access to an object without changing the interface clients already expect.

Java Example

Three structural wrapper shapes in one Java sketch

Java example: three wrappers that look similar from far away
package org.javaomnibus.ecommerce.gof.structural;

public final class StructuralWrapperExamples {
    ShipmentTracker trackingAdapter() { return null; }
    OrderPlacementFacade orderFacade() { return null; }
    CustomerProfileRepository securedRepositoryProxy() { return null; }
}
Code Walkthrough

Step by step

  1. trackingAdapter exists because a legacy carrier API does not fit the domain-facing interface.
  2. orderFacade exists because clients want one simpler entry point into several subsystem steps.
  3. securedRepositoryProxy exists because repository access needs mediation without changing the repository contract.
  4. All three wrap something, but the reason for the wrapper is what matters.
Real-World Usage

Where this comparison helps

  • Integration boundary design reviews
  • Explaining wrapper intent in service-layer code
  • Refactoring confusing wrapper classes into clearer structural roles
Comparison Table

Comparison Table

Pattern Main force What stays the same Typical Java example
AdapterInterface mismatchClient expectationWrapping a legacy SDK client
FacadeSubsystem complexitySubsystem internals behind the scenesOrder placement orchestration entry point
ProxyAccess mediationTarget interfaceSecurity or lazy-loading wrapper
Trade-Offs

How to use this guidance responsibly

Comparison pages make pattern choice easier, but wrapper-heavy systems still require careful naming and architecture discipline. The goal is clarity of intent, not just picking a label.

Common Mistakes

Where teams still get confused

  • Calling a subsystem simplifier an adapter
  • Using proxy language when the wrapper actually translates interfaces
  • Building a facade that grows into an all-knowing service object
  • Ignoring that a direct redesign may sometimes be better than another wrapper layer
Related Pages

Compare the detailed structural pages


What To Read Next