Simplified Adapter (Structural Pattern)
đ Why Adapter Pattern
Architect Spidey wants to enhance her movie review app by showing IMDb reviews. Itâs a smart way to add more content and engage users.
But thereâs a slight twistâIMDb provides the reviews in a format that doesnât match what Spidey is already using. If she tries to plug it in directly, itâll break things and require rewriting a lot of code.
She remembers visiting her grandparents, where a nearby spider from a different community spoke a language she couldnât understand. Thankfully, a friend who knew both languages stepped in and translated, helping them understand each other.
Now, Architect Spidey hopes for something similarâa translator to help her app understand IMDbâs review format without changing the system.
Thatâs precisely what the Adapter Pattern does: it acts as a bridge between two incompatible interfaces, allowing them to work together seamlessly.
đĽď¸ Outline
An Adapter is a wrapper object that helps two incompatible objects talk to each other. It provides an interface friendly to one object and inside, it translates the call to match the other objectâs way of working.
Adapter â A translator plug that makes two mismatched objects talk.
đ Adapter In Real World
When you travel to Europe, your phone charger plug doesnât fit the wall sockets there. So, you use a travel adapter â a small tool that connects the two different systems and makes them work together.
đ Solution
Architect Spidey already uses a service that follows a MovieReviewService protocol, which returns a collection of MovieReview structs:
To fetch data from IMDb, she has a new service called IMDbReviewService, which returns a collection of IMDBReview objects matching the format received from the IMDb API
However, the formats donât match, and integrating IMDB reviews directly into her app would mean changing existing logic. So, she applies the Adapter Pattern by creating a class called IMDBReviewAdapter.
Hereâs whatâs happening:
  The adapter wraps the IMDbReview object, handling all the conversion logic behind the scenes.
  The original IMDb data structure doesnât even know itâs being adapted.
  The client (the movie review app) calls fetchMovieReviews(movieName) and receives reviews in the MovieReview format it already understands â no extra work required.
Just like a travel adapter helps your charger fit into a foreign socket, an adapter in software helps external data (like IMDb reviews) fit into your app without changing either.
Adapter Components
Adapter(Translator/Wrapper):
The Adapter is a wrapper class that makes two incompatible objects work together by converting the interface of one into the interface expected by the other.
In our example, IMDbReviewAdapter plays the role of the adapter. It wraps around an IMDbReviewService object (which has a different structure) and translates it into the MovieReview format that the app understands â just like a translator plug for mismatched devices.
Target:
This is what the client code is expecting. In terms of the translator, the language is understood by the existing system. In our example, the MovieReviewService struct is the Target.
Client(Consumer):
The Client is the part of the application that needs some service but only understands a specific format. It doesnât know (or care) how the data comes in â it just wants it in the format it expects. In Spideyâs case, the movie review list page is the Client.
Adaptee:
The Adaptee is the class that already has the functionality, but its interface is not what the client expects. It cannot be used directly by the client. In this case, IMDbReviewService is the Adaptee.
đ Where To Apply
  Use Adapter to unify different formats for the same function
The Adapter pattern provides a uniform interface and acts as a translator for a client.
Imagine a news-reading application that pulls in articles from various news sources. Each source offers data in a different format: some use JSON, while others provide data in XML. The Adapter pattern can be applied here to convert each news sourceâs response into a uniform format. The application doesnât need to worry about the specific format from sources.
  Use Adapter to standardize the interface
It might look similar to the point above but the key difference lies in why youâre applying the Adapter pattern. Instead of using the Adapter only when thereâs a mismatch in data formats, you apply it as a standard integration layer for any third-party service, regardless of format compatibility. it can greatly enhance your systemâs maintainability.
Imagine your application initially uses Apple Maps. All the logic and UI depend on its methods and data format. But later, due to bugs or limitations, you decide to switch to Google Maps.
đ To Gain Something, You Must Trade Off
Design patterns are solutions to common problems in software design. They provide a structured approach to solving issues, much like a blueprint. While itâs possible to solve a problem without using a design pattern, applying one offers better flexibility, scalability, and maintainability. However, each benefit comes with its trade-off. Letâs examine this concept in the context of the Adapter pattern.
Maintainability vs Complexity
The adapter class decouples the client code from a service & its dependency, so if the adaptee changes its format (JSON/XML) or behaviour, you only need to update the Adapterânot the entire application. This makes the system easier to maintain and scale dependencies without much risk.
Introducing an Adapter adds a new class to your system, which means thereâs one more layer to understand. This can make the code slightly more complex to navigate, especially for new developers or when debugging.
Flexibility vs Extra Layer
The adapter pattern provides plug-and-play integration for external services. You can easily swap out one service for another without touching the main application code. This greatly improves flexibility and maintainability
While Adapters help incompatible objects or services work together, too many adapters can clutter your codebase. Think of it like a drawer full of travel plug adaptersâvery handy, but it quickly becomes messy and hard to manage when every device or region needs a slightly different plug. Likewise, having an adapter for every third-party service or object variation may lead to extra classes to create, track, and test.
Reusability vs Performance overhead
The Adapters allow developers to reuse existing code that might have incompatible interfaces. So instead of rewriting or modifying the original code, an adapter can translate its interface to match the new systemâs requirements.
Adapters introduce an extra layer of indirection, which may affect performance, especially when dealing with large volumes of data transformation.
đ Some Interesting Fact
  Adapter & Facade Pattern
The adapter pattern allows allow two incompatible interfaces to work together. It acts as a translator or wrapper between objects with different interfaces. The Adapter pattern typically focuses on a single object (the adaptee) that needs to be made compatible with the system. It wraps this one object and converts its interface into one that the client code expects.
On the other hand, Facade provide a simplified interface to a complex system. It acts as a single entry point hiding internal complexities of framework or library. A Facade usually coordinates with multiple objects.
  Adapter & Proxy Pattern
The Proxy pattern adds a layer of control without changing the actual object. Â The Proxy Pattern is like a security gate at the entrance of a building. It controls who can enter, logs their entry, and may even delay access until certain conditions are me. This pattern is useful for adding security, logging, caching, or lazy initialization.
On the other hand, the Adapter pattern does not introduce any custom behavior or restrictions. It simply translates or simplifies the interface to make an existing object compatible with what the client expects.
  Adapter & Mediator Pattern
The Adapter pattern helps two incompatible objects communicate by acting as a translator.
The Mediator pattern, on the other hand, acts as a central coordinator that manages communication between multiple objects. It is like an air traffic controller â ensuring that all planes (objects) communicate through a single point to avoid confusion and collision.
Code Example (Swift)
Target Interface:
1
2
3
protocol MovieReviewService {
func fetchMovieReviews() -> [MovieReview]
}
Movie review:
1
2
3
4
5
6
struct MovieReview {
var username: String
var rating: Double
var review: String
var date: String
}
Adaptee Interface:
1
2
3
4
5
struct IMDbService {
func fetchIMDbReviews(name: String) -> [IMDbReview] {
//Fetch movie and return IMDbReview objects
}
}
1
2
3
4
5
6
7
struct IMDbReview {
var rating: Double
var author: String
var review: String
var category: String
var postedOn: String
}
The Adapter:
1
2
3
4
5
6
7
8
9
10
11
12
struct IMDbAdapter: MovieReviewService {
let imdbService: IMDbService
func fetchMovieReviews(name: String) -> [MovieReview] {
let imdbReviews = imdbService.fetchIMDbReviews(name: name)
return self.mapToMovieReviews(imdbReviews)
}
private func mapToMovieReviews(_ reviews: [IMDbReview]) -> [MovieReview] {
//Map logic
}
}
Client Code:
1
2
3
4
5
6
func displayMovieReview(name: String, service: MovieReviewService) {
let reviews = service.fetchMovieReviews(name: name)
}
// Usage
let adapter = IMDbAdapter(imdbService: IMDbService())
displayMovieReview(name: "Avatar", service: adapter)
Comments powered by Disqus.