OOD Fundamentals
OOP Foundations
SOLID Principles
Creational Patterns
Behavioral Patterns
Classic OOD Problems: Part 1
Classic OOD Problems: Part 2
Adapter and Facade
You have built your system around a clean interface. Then a new requirement arrives: integrate a third-party library whose API looks nothing like yours. Do you rewrite your code to match the library? That defeats the purpose of having a stable interface. Do you modify the library? You do not own it. The Adapter pattern solves this by creating a thin wrapper that translates between your interface and the incompatible one.
Think of a travel power adapter. Your laptop charger has a US plug. The wall outlet in Europe accepts a different shape. The adapter sits between them: it does not change how your charger works or how the outlet delivers power. It simply translates one physical interface into another.

How It Works
The pattern has three roles:
Target: the interface your client code expects. Your application is written against this interface and should never need to change.
Adaptee: the existing class with an incompatible interface. This might be a third-party library, a legacy module, or an external service SDK.
Adapter: a new class that implements the Target interface and internally delegates to the Adaptee. The adapter translates method names, parameter formats, and return types so the client sees a familiar interface while the adaptee does the actual work.
The client calls player.play("movie.mp4") without knowing or caring that a VLC library handles the work underneath. If you later switch to a different media backend, you write a new adapter: the client code never changes.
When to Use It
Adapters appear whenever you integrate code you do not control. Wrapping a payment gateway API, connecting to a legacy database driver, or making a new logging framework match your existing logging interface are all adapter use cases. The rule of thumb: if you cannot change the interface and you cannot change your client code, put an adapter between them.
In OOD interviews, reach for the Adapter pattern whenever the problem says 'integrate with an external system' or 'support multiple providers.' It shows you understand interface-driven design and know how to isolate third-party dependencies.
There are two ways to build an adapter: wrap the adaptee inside the adapter (object adapter) or inherit from the adaptee (class adapter). The choice matters because it determines how tightly coupled your adapter is to the adaptee.

Object Adapter (Composition)
The object adapter holds a reference to the adaptee and delegates calls to it. This is the preferred approach in almost every scenario.
Why prefer it? First, you can adapt any subclass of the adaptee without writing a new adapter: pass in any object that has the right methods. Second, you can swap the adaptee at runtime. Third, the adapter and adaptee have separate class hierarchies, so changes to the adaptee's internal structure do not break the adapter.
Class Adapter (Inheritance)
The class adapter extends the adaptee and implements the target interface simultaneously. In languages that support multiple inheritance (like C++ or Python), the adapter inherits from both.
This approach is simpler: no delegation, no wrapper object. But it has serious drawbacks. The adapter is locked to one specific adaptee class. It cannot adapt subclasses without creating more adapters. And in languages like Java that do not support multiple inheritance of classes, the class adapter is simply not possible.
The Verdict
Use object adapters by default. The flexibility of composition outweighs the slight simplicity of inheritance. Class adapters are a niche tool for performance-critical scenarios in languages that support multiple inheritance, and even then the benefit is marginal.
Object adapter vs class adapter is really just composition vs inheritance in disguise. The same reasoning applies: composition is more flexible, less coupled, and works across language boundaries. If you remember one rule, remember this one.
Where the Adapter translates one interface into another, the Facade creates a simpler interface for an entire subsystem. The difference is scope: an adapter wraps one class, a facade wraps many.
Consider a home theater system. To watch a movie, you need to turn on the projector, set the input to HDMI, power up the sound system, set it to surround mode, start the streaming player, dim the lights, and close the curtains. That is seven objects with seven different interfaces. A facade wraps all of them behind a single watchMovie() method.

Structure
The facade holds references to all subsystem objects and provides high-level methods that coordinate them. The subsystem classes do not know the facade exists: they are not modified in any way. The facade is purely additive.
The client's code drops from seven coordinated calls to one: theater.watch_movie("Inception"). The subsystem is still fully accessible if needed: the facade does not hide it, just provides a shortcut.
When to Use It
Facades appear in every codebase that wraps a complex library or subsystem. Database access layers that bundle connection, query execution, and result mapping into one call. Email services that coordinate SMTP configuration, template rendering, and delivery tracking. Cloud SDK wrappers that simplify the 15-parameter calls into sensible defaults with 2-3 parameters.
The key insight: a facade does not add new functionality. It reorganizes existing functionality into a more convenient shape. If you find your facade computing business logic or transforming data, it has grown beyond its role.
Real subsystems are messy. A cloud storage SDK might have classes for authentication, bucket management, object operations, access policies, encryption configuration, and transfer acceleration. Using the raw SDK means understanding dozens of classes and calling them in the right order. A facade distills the common use cases into a handful of methods.
Layered Facades
Complex systems often benefit from multiple facade layers. A low-level facade wraps the raw SDK. A mid-level facade adds application-specific defaults. A high-level facade exposes only the operations your application actually uses.
Each layer simplifies the one below it. The UserAvatarService knows that avatars go in a specific bucket, are resized to 200x200, and use a predictable key format. It does not expose any of that complexity: it just offers uploadAvatar(userId, imageFile).
Facade vs Service Layer
A facade and a service layer look similar, both provide a simplified interface to complex functionality. The distinction matters:
A facade wraps an existing subsystem without adding business logic. It coordinates calls and provides convenient defaults. Removing the facade would not lose any capability: just convenience.
A service layer implements business logic by orchestrating domain objects and infrastructure. It makes decisions, enforces rules, and manages transactions. Removing it would lose functionality.
When you catch yourself writing if/else logic inside a facade, consider whether it has grown into a service layer. That is not necessarily wrong: just recognize the shift and name the class accordingly.
A facade that grows business logic silently becomes a service layer. This is fine if intentional, but dangerous if unnoticed, you end up with business rules scattered across a class that everyone treats as a simple wrapper, making bugs hard to find and tests hard to write.
Adapter and Facade both wrap existing code behind a different interface. Interviewers often ask you to compare them. The difference is straightforward once you see it:
Adapter changes the interface of one existing class to match what the client expects. It is about compatibility.
Facade provides a simplified interface to a group of classes in a subsystem. It is about convenience.

Side by Side
| Dimension | Adapter | Facade |
| Problem | Incompatible interface | Complex subsystem |
| Scope | Wraps one class | Wraps many classes |
| Intent | Make it compatible | Make it simple |
| New functionality? | No, translates only | No, coordinates only |
| Client knowledge | Client does not know about adaptee | Client may still access subsystem |
Combined Usage
In practice, adapters and facades often work together. A facade might use adapters internally. For example, a PaymentFacade coordinates order validation, payment processing, and receipt generation. Internally, the payment processing step uses a StripeAdapter that implements the PaymentProcessor interface. The facade simplifies the workflow; the adapter handles the interface mismatch.
This layered approach gives you both benefits: a simple high-level interface (the facade) and vendor isolation (the adapter). If you switch from Stripe to PayPal, you replace the adapter. If you add fraud detection to the workflow, you add it to the facade.
How to Decide
Ask yourself one question: am I wrapping one thing or many things?
If the answer is one thing with an incompatible interface, use an Adapter. If the answer is many things that need a simpler entry point, use a Facade. If you need both, which is common, use both.
Practice Problems
Apply the Adapter and Facade patterns by implementing these systems. Start with the media player adapter to practice basic interface translation, then tackle the home theater facade for subsystem coordination, and finish with the payment gateway adapter which combines both patterns.
Loading problem...
Loading problem...
Loading problem...