Practical Aspects of the Adapter Pattern

Share this article

Software development is improved every day by new concepts, methodologies, and high quality libraries and frameworks. But even with all these improvements, we cannot prevent change in software development. You may think that your system is designed perfectly to cater to all of its requirements, but there will always be a change request that ruins your perfect design. We have to be prepared for all possible changes as developers. The Adapter pattern is a design pattern which is commonly used to manage changes in development. Throughout this article we’ll be looking at the usage and benefits of the patterns using real world applications.

What is the Adapter Pattern?

The Adapter design pattern simplifies concerns by adapting to changes in existing functionalities as well as building new functionalities. In short, we can define an adapter as an interface which helps integrate incompatible components. Assume we have a mobile phone which is used to access an email account to send emails. The phone and email application act as separate components which get connected through the Internet.

adapt-01

Now assume that we travel to a place where an Internet connection is not available for the phone. How do we access email in this situation? We need an adapter which connects our mobile phone to an email application. Let’s take a look at the features expected from such an adapter:
  • Enable an Internet connection between mobile phone and email application.
  • Access email application API to send an email.
Considering the requirements, we can choose IFTTT as the adapter. IFTTT is a service that supports automating tasks with popular API’s. Thus we can use IFTTT as the adapter as shown in the illustration below.

adapt-02

In this solution, we send a SMS to the IFTTT service containing the email text. There is no need for an Internet connection to send an SMS to an international number. Then the IFTTT service gets the message contents and initializes the recipe (recipes need to be configured prior) to send an email using the email application’s API. IFTTT has access to the email API as well as an Internet connection to the email application, completing both requirements expected from the adapter. IFTTT acts as the adapter to integrate our phone and email application which was in an incompatible state due to the unavailability of the Internet. I think by now you should have a clear understanding about the functionality of the Adapter pattern in the real world. Before we move further into the implementation, though, let’s take a look at the definition of the pattern as given by Wikipedia:
In computer programming, the adapter pattern is a design pattern that translates one interface for a class into a compatible interface.An adapter allows classes to work together that normally could not because of incompatible interfaces, by providing its interface to clients while using the original interface.

Understanding Adapter Pattern Implementation

It would be ideal to make use of a practical scenario to understand the process and components of the Adapter pattern, so assume that we have a common interface for email subscriptions for our website. The following code contains the implementation of an email subscription interface.
<?php
interface EmailSubscribe
{
    public function subscribe($email);
    public function unsubscribe($email);
    public function sendUpdates();
}
Developers and email service providers can implement this interface to provide email subscription classes for each of the email providers such as Feedburner, Mailchimp, etc. The following code contains a sample implementation for one service and the initialization of the sendUpdates() method.
<?php
class FeedburnerEmail implements EmailSubscribe
{
    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function sendUpdates() {
   	 // Get Available Subscribers
   	 // Get Website Updates
   	 // Send Emails
    }
}

$feedburner_email = new FeedburnerEmail();
$feedburner_email->sendUpdates();
Now assume Feedburner decides to change its library with the latest version.
<?php
class FeedburnerEmailVersion2
{
    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function getSubscribers() {
   	 // Return Subscribers
    }

    public function sendEmails($subscribers) {
   	 // Get Website Updates
   	 // Send Emails
   	 echo "emails sent today";
    }
}

$feedburner_email = new FeedburnerEmailVersion2();
$subscribers = $feedburner_email->getSubscribers();
$feedburner_email->sendEmails($subscribers);
According to the preceding code, new methods are added and existing functionality is modified in the new version of the library. The initialization code has changed and the latest version of Feedburner has become incompatible with the EmailSubscribe interface. We cannot implement the common interface, and therefore we need an adapter to make the library compatible with the original interface to keep consistency in our code. Since the current version is not implementing the interface, we have no choice other than to create the adapter based on the interface.
<?php
class FeedburnerAdapter implements EmailSubscribe
{
    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function sendUpdates() {
   	 $feedburner = new FeedburnerEmailVersion2();
   	 $subscribers = $feedburner->getSubscribers();
   	 $feedburner->sendEmails($subscribers);
    }
}

$feedburner_email = new FeedburnerAdapter();
$feedburner_email->sendUpdates();
The FeedburnerAdapter adapter initializes the Feedburner email library inside it’s sendUpdates() method and reconstructs the previous implementation by calling new methods in the latest version of the library. Now our application and the Feedburner library communicate through the standard interface of FeedburnerAdapter. Our application does not know that the implementation has changed and an adapter is working in place of the original library class. Developers can call the standard set of methods without making any change to their original code. Now it’s time for understanding theoretical aspects of the Adapter pattern using its class diagram.

adapt-03

Usually we have a Client, Target, and Adaptee in our application, and the Adaptee class implements the Target interface. In situations where Client and Adaptee become incompatible, we create a new class called Adapter and place it in between Target and Adaptee to make the components compatible with each other. The diagram above contains the original design of the Adapter pattern to use in best-case scenarios. Interfaces are not used widely in PHP projects but this doesn’t mean that you cannot use the Adapter pattern. As long as some component integrates incompatible interfaces, it can be considered an adapter. In my last article on Opauth, we discussed about using a strategy class in the Opauth library. It also acts as an adapter, even though it doesn’t implement any interfaces. The strategy adapter made the open authentication libraries compatible with the core Opauth library.

Who Develops the Adapter Class?

When we’re in need of an adapter, either we can create it as developers or we can ask the vendor to provide the adapter. It depends on the situation and type of the project we’re working on. If we are developing applications by using common third party libraries, then we will be responsible for creating adapters to suit the requirements. On the other hand, we might be developing a large scale application and expect the vendors to develop libraries specially for our application. In such scenarios, if the vendors changes their library then they should provide the Adapter as well.

Adapter Pattern – The Wrong Way

Many experienced developers think that the Adapter pattern is used to fix poorly designed systems. Depending on the situation, we might have to agree with that. But let’s consider a slightly modified version of the email subscription example we discussed previously. Assume that two teams have been assigned to develop Feedburner and Mailchimp classes separately based on the original interface we used earlier.
<?php
class FeedburnerEmail implements EmailSubscribe
{
    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function getSubscribers() {
   	 // Returns list of subscribers
    }

    public function sendUpdates() {
   	 $this->getSubscribers();
   	 // Get Website Updates
   	 // Send Emails
    }
}
<?php
class MailchimpEmail implements EmailSubscribe
{
    public $subscribers;

    public function subscribe($email) { }
    public function unsubscribe($email) { }

    public function getSubscribers() {
   	 $this->subscribers = "List of subscribers";
    }

    public function sendUpdates() {
   	 $subscribers = $this->subscribers;
   	 // Get Website Updates
   	 // Send Emails
    }
}
Even though both classes are compatible with the target interface, there is an incompatibility between the client and these two classes. Consider the initialization code to understand this better:
<?php
$email = FeedburnerEmail();
$email->sendUpdates();

$email = MailchimpEmail();
$email->getSubscribers();
$email->sendUpdates();
The code from Team 2 doesn’t match with the client initialization code and hence becomes incompatible. We need an adapter to fix the issue for the Mailchimp class. This is considered a bad use of Adapter pattern since we could have planned the interface properly to avoid such incompatibility issues.

Adapter Pattern – The Right Way

Adapters are mostly used in situations where we work with third-party libraries or create a new functionality which is considerably different from the original requirements. So, let’s consider the following scenario for effective use of adapters. Email subscriptions is working perfectly on our website. Due to the success of subscriptions, management is planning to implement Twitter subscriptions for the site. Currently, when a user subscribes through email, he or she will get email notifications about updates in website content. With the new requirement, basically the user subscribes by authenticating their Twitter account for our website. Whenever site is updated, new tweets will be created in their tweet stream about the update. The following code contains the Twitter library for this implementation.
<?php
class TwitterService
{
    public function authenticate($username) {}
    public function deauthenticate($username) {}

    public function tweet($message,$user) {
        // Update  wall with new tweet
    }

    public function getUpdates() {
        // Return Updates
    }

    public function getFollowers() {
        // Return followers
    }
}
There is no way we can make TwitterService compatible with a target interface or client with its original implementation. But we can see that logic of the class is similar to EmailSubscription. Therefore, we can effectively use an adapter class in this situation to make TwitterService compatible with the client without changing client code. Let’s look at the implementation of the TwitterAdapter class.
<?php
class TwitterAdapter implements EmailSubscribe
{
    public function subscribe($username) { }
    public function unsubscribe($username) { }

    public function sendUpdates() {
        $tw_service = new TwitterService();
        $updates = $tw_service->getUpdates();
        $subscribers = $tw_service->getFollowers();
        $tw_service->tweet($updates,$subscribers);
    }
}

$twitter_subscribe = new TwitterAdapter();
$twitter_subscribe->sendUpdates();
The TwitterAdapter class implements our target interface with original email subscription related functionalities. Internally it creates an object of TwitterService and makes the tweet function compatible with sendUpdates() by calling the necessary functions and returning the output expected by the client. The initialization code seems similar to the previous code. Therefore, the client class doesn’t know that Twitter service sends a tweet on updates instead of an email. The client class keeps calling the sendUpdate() method for all the services and the respective updating techniques will be executed through adapters.

Summary

Throughout this article we’ve looked at the Adapter pattern and tried to to understand the effective uses of it through some practical examples. We learned that there are both good and bad uses of the Adapter pattern, and now it’s up to you to decide when to go with adapters. Let me know about the practical scenarios which you faced in application development and how you provided a solution through adapters in the comments below. Image via Fotolia

Frequently Asked Questions about the Adapter Pattern

What is the main purpose of the Adapter Pattern in software design?

The Adapter Pattern is a structural design pattern that allows incompatible interfaces to work together. It acts as a bridge between two incompatible interfaces by converting the interface of one class into another interface that clients expect. This pattern is particularly useful when you want to use an existing class, but its interface doesn’t match the one you need. It allows classes to work together that couldn’t otherwise because of incompatible interfaces.

Can you provide a real-world example of the Adapter Pattern?

A real-world example of the Adapter Pattern is the case of memory card readers. Different memory cards have different interfaces, but a card reader can adapt each type of memory card to a standard USB interface that can be used by a computer. The card reader acts as an adapter, allowing memory cards and computers to work together despite their incompatible interfaces.

How does the Adapter Pattern differ from the Decorator Pattern?

While both the Adapter and Decorator Patterns are structural design patterns, they serve different purposes. The Adapter Pattern is used to make two incompatible interfaces compatible, allowing them to work together. On the other hand, the Decorator Pattern is used to add new functionality to an existing object, without altering its structure. The Decorator Pattern is more about adding responsibilities to the object, while the Adapter Pattern is more about making objects fit others’ expectations.

What are the benefits of using the Adapter Pattern?

The Adapter Pattern offers several benefits. It enhances the reusability and flexibility of the software. It allows classes with incompatible interfaces to work together. It also promotes loose coupling between the software components, which makes the software more modular and easier to understand, maintain, and test.

Can the Adapter Pattern be used with other design patterns?

Yes, the Adapter Pattern can be used in conjunction with other design patterns. For example, it can be used with the Factory Pattern to create the adapter instances. It can also be used with the Decorator Pattern to add functionality to the adapter.

What are the drawbacks of the Adapter Pattern?

While the Adapter Pattern has many benefits, it also has a few drawbacks. It can increase the complexity of the code, especially when used extensively. It can also lead to problems if the adapter class isn’t correctly designed or if it doesn’t correctly mimic the behavior of the target interface.

When should I use the Adapter Pattern?

The Adapter Pattern should be used when you want to use an existing class, but its interface doesn’t match the one you need. It’s also useful when you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don’t necessarily have compatible interfaces.

Can you provide a simple code example of the Adapter Pattern?

Yes, here’s a simple example in Java:

// Existing interface
public interface OldInterface {
void oldMethod();
}

// Adapter class
public class Adapter implements NewInterface {
private OldInterface oldObject;

public Adapter(OldInterface oldObject) {
this.oldObject = oldObject;
}

public void newMethod() {
oldObject.oldMethod();
}
}

In this example, the Adapter class adapts the OldInterface to the NewInterface by wrapping an instance of a class that implements the OldInterface and providing a method that matches the NewInterface.

How does the Adapter Pattern relate to the Single Responsibility Principle?

The Adapter Pattern adheres to the Single Responsibility Principle, which states that a class should have only one reason to change. The adapter class has the single responsibility of adapting the interface of one class to another, allowing them to work together.

Can the Adapter Pattern be used in multithreaded applications?

Yes, the Adapter Pattern can be used in multithreaded applications. However, care must be taken to ensure that the adapter class is thread-safe, especially if the adapted class is not thread-safe. This may involve adding synchronization mechanisms to the adapter class.

Rakhitha NimeshRakhitha Nimesh
View Author

Rakhitha Nimesh is a software engineer and writer from Sri Lanka. He likes to develop applications and write on latest technologies. He is available for freelance writing and WordPress development. You can read his latest book on Building Impressive Presentations with Impress.js. He is a regular contributor to 1stWebDesigner, Tuts+ network and SitePoint network. Make sure to follow him on Google+.

Advanced
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week