In the last article, we looked at how the facade design pattern can be employed to simplify the employment of any large and complex system using only a simple facade class.
In this article, we will continue our discussion on design patterns by taking a look at the adapter design pattern. This particular pattern can be used when your code is dependent on some external API, or any other class that is prone to change frequently. This pattern falls under the category of "structural patterns" because it teaches us how our code and our classes should be structured in order to manage and/or extend them easily.
Again, I'd like to reiterate that design patterns have nothing new over traditional classes. Instead, they show us a better way to structure our classes, handle their behavior, and manage their creation.
The Problem
class Twitter { public function __construct() { // Your Code here // } public function send($msg) { // Posting to Twitter // echo $msg; } } $twitter = new Twitter(); $twitter->send('Posting on Twitter');
In the above code, you can see that we are utilizing a Twitter class to simply tweet a message. Here, we are directly creating the object of the Twitter API and posting tweets on Twitter. You have this code scattered in multiple places. So we can see that the code is using the $twitter->send('Posting on Twitter');
method to tweet.
Some time ago, Twitter changed the API method name from send
to sendTweet
. This should clearly indicate a problem for those of us who have been using the send
method. Specifically, we need to change all send
method calls to sendTweet
. Imagine the amount of code we need to change and the time we need to spend on testing each of the features once again.
The Solution
One solution to this problem is to use the adapter design pattern.
According to Wikipedia:
In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used from another interface. It is often used to make existing classes work with others without modifying their source code.
In this case, we should create one wrapper interface which makes this possible. We will not make any changes in the external class library because we do not have control over it and it may change any time.
Let's dig into the code now, which shows the adapter pattern in action:
// Concrete Implementation of Twitter Class class Twitter { public function __construct() { // Your Code here // } public function send($msg) { // Posting to Twitter // echo $msg; } } // Simple Interface for each Adapter we create interface socialAdapter { public function send($msg); } class twitterAdapter implements socialAdapter { private $twitter; public function __construct(Twitter $twitter) { $this->twitter = $twitter; } public function send($msg) { $this->twitter->send($msg); } }
Study the code above and you should be able to tell that we have not introduced any changes into the main Twitter
class. Instead we have created one interface for our social adapter and one adapter class for Twitter.
And so afterward we have made the object of the adapter class instead of the main Twitter
class. While creating an object of adapter class we will pass the object of the main Twitter
class as an argument, so that adapter class can have a reference to the main class and it can call the required methods of the main Twitter
class.
Let's find out how we can utilize this method directly:
// Client Code $twitter = new twitterAdapter(new Twitter()); $twitter->send('Posting to Twitter');
Now imagine Twitter changes its method name from send
to sendTweet
. Then we just need to make changes in twitterAdapter
. Just have a look at the revised adapter code, which has just one change.
class twitterAdapter implements socialAdapter { private $twitter; public function __construct(Twitter $twitter) { $this->twitter = $twitter; } public function send($msg) { $this->twitter->sendTweet($msg); } }
So just one change and we are there.
Adding a New Adapter
At this point, we've seen how we can use the adapter design patten to overcome the aforementioned scenarios. Now, it's very easy to add a new class dependent on the existing adapter. Let's say the Facebook API is there to update the status from the API itself.
Then instead of using the Facebook class directly, we should be applying the same adapter pattern we just used for Twitter.
// Concrete Implementation of Twitter Class class Facebook { public function __construct() { // Your Code here // } public function updateStatus($msg) { // Posting to Facebook // echo $msg; } } // Facebook Adapter class facebookAdapter implements socialAdapter { private $facebook; public function __construct(Facebook $facebook) { $this->facebook = $facebook; } public function send($msg) { $this->facebook->updateStatus($msg); } } // Client Code $facebook = new facebookAdapter(new Facebook()); $facebook->send('Posting to Facebook');
As you can see, the same principles apply. You define a method that's available to third-party classes and then, if a dependency changes its API, you simply change the dependent class without exposing its external interface.
Conclusion
A great application is constantly hooked into other libraries and APIs, so I would propose that we implement the adapter method, so that we do not experience any trouble when a third-party API or library changes its code base.
I have tried my best to provide an elementary and yet useful example to demonstrate the adapter design pattern, but if you have additional comments or questions, please don't hesitate to add them in the feed below.