Hi all!
Today I am starting a post series about Design Patterns. I have wrote about them a lot but only suggesting you to learn about to be a better developer. I am studying them, so nothing better to write about and improve my knowledges too.
I only ask you to read everything to understand the concepts behind the patterns. You have to understand them to know where you can use one or another.
The Adapter Pattern
The Adapter Pattern is exactly what it is, an adapter. It works to allow you to adapt your code to a new requirement that did not exist before “now”.
Let’s suppose you are working on a project where you have a website and have to allow users to write on the company’s Twitter profile. You can have the follow situation:
- Post.php: Your post class. This is the Post object, where you have text and a URL, for example.
- Twitter.php: The Twitter class. It can be a class created by you or for example a class you got in Packagist website.
Good. So, you could write a code like that:
<?php // creating a post and posting to Twitter $post = new Post(); $post->description = 'My first post to Twitter. Just for fun!'; $post->url = 'http://juniorgrossi.com'; $post->send(); // inside Post class class Post { // ... code and more code public function send() { $text = $this->description . ' ' . $this->url; // some Twitter class $twitter = new Twitter(); // authenticate and more ... $twitter->tweet($text); } }
Ok! That works! Solve your problem perfectly, so it is a very good approach.
But now someone tells you that you have to change the code and choose to post to Twitter or Instagram. Ok! It’s easy!
<?php // changing the Post class class Post { public function send($service = 'twitter') { if ($service == 'twitter') { $twitter = new Twitter(); // ... $twitter->tweet(); } elseif ($service == 'instagram') { $instagram = new Instagram(); // ... $instagram->postToInstagram(); } } }
Ok! It works! But you can do better than that. Now it is the opportunity to you to use a pattern, the Adapter Pattern. You can adapt you code to a generally solution, just changing the service you want. If you have now to include Facebook too, you will do that easy, and not change the send()
method again and include one more if
condition. Do that is a very bad idea!
Creating the Service Interface
Here you have 2 different services: Twitter and Instagram. The first one uses a method to “post” called tweet()
and the second another method called postToInstagram()
. First you have to create a pattern, with one method that will be responsable to post to the service, don’t matter what is. It’s the chance you have to create a Interface. Take a look!
interface ServiceInterface { public function authenticate(array $options); public function post($text, $url); }
Just that. Here is the secret of your developer’s life! Use Interfaces for everything! This interface has, for example, two methods: one to authenticate in the service and another to post the text to the service. Now you will create two more classes (pay attention them implements the interface you’ve created):
class TwitterService implements ServiceInterface { protected $service; public function __construct() { $this->service = new Twitter(); } public function authenticate(array $options) { $apiKey = $options['api_key']; // ... $this->service->authenticateUsingSomeMethod($apiKey); // ... } public function post($text, $url) { // ... $this->service->tweet($text . ' ' . $url); // ... } } class InstagramService implements ServiceInterface { protected $service; public function __construct() { $this->service = new Instagram(); $this->service->someAnotherMethodYouHaveToCall(); } public function authenticate(array $options) { // ... $this->service->authenticateWithInstagramClass(); // ... } public function post($text, $url) { // ... $this->service->postToInstagram($text); // ... } }
And let’s change the Post.php
class for the last time 😀
class Post { protected $service; public function setServiceAdapter(ServiceInterface $service) { $this->service = $service; } public function send() { $this->service->post($this->description, $this->url); } }
Now, to use the Post class you have to instantiate, set description
and url
properties, provide the ServiceInterface
object and call the send()
method:
// creating a post and posting to Twitter, Instagram or Facebook $post = new Post(); // if want to use Twitter $post->setServiceAdapter(new TwitterService()); // OR // if want to use Instagram $post->setServiceAdapter(new InstagramService()); // OR maybe // if want to use Facebook or another social network adapter $post->setServiceAdapter(new FacebookService()); $post->description = 'My first post to Twitter. Just for fun!'; $post->url = 'http://juniorgrossi.com'; $post->send();
This way you can create new adapters without change your Post
code. Your Post
class use a Interface and only classes that implements that interface will have those methods, the methods that Post
class uses to send a post.
That’s all! I wish explain a little about the Adapter Pattern. If you can improve this post with another example or correcting me with something please tell me. You are welcome to contribute and share!
Thanks for reading!
Hi Grossi.
Nice post!
Thanks for taking the time to write it.
I’m a little bit confused here: isn’t that the Strategy Pattern?
When will you write more about the others patterns?
Thx!
Thanks @Rod! Be welcome!
Hi, thx for this article.
I’m trying to find a classic participants of a Adapter pattern in your example.
I think it is the following :
Target – the first version of Post class, some client code use it.
Adaptee – TwitterService ,InstagramService
Adapter – The second version of Post class
Client – Code that use Post class
Am I right with this classic approach )?
hi @max! I think as @xu said, this is not the best option to explain about the adapter pattern. this is maybe more close to the factory pattern. I have to update this post to a better approach but I thank you for the comment and interest on the blog. thanks
Thanks dude,
great explanation, I thought adapters are tough and vague. But you made this simple.
And I think your example is more like a “Factory” pattern.
hi @xu! you’re right! thanks
Hi Kiran
Thanks for the hardwork.
However, I find your explanation of adapter pattern is confusing. Referring to http://en.wikipedia.org/wik…. Adapter pattern translates one interface for a class into a compatible interface. However in your example, there is no compatible issue.
Correct me if I am wrong. I would explain it this way:
in Post.php(“client”, imagine Post.php is someone else’s work, he is only aware of ServiceInterface), client has been calling Twitter and Instagram services via $post function without any problem , because they all implement the common ServiceInterface interface.
Now we need to introduce Facebook to the system. Suppose it is impossible to make the Facebook service to implement ServiceInferface. Here we have an incompatible issue. We need to make the Adaptee(Facebook service) compatible with Target(ServiceInterface). So we use Adapter pattern:
class FacebookToServiceAdapter implements ServiceInterface {
protected $service;
public function __construct()
{
$this->service = new Facebook();
$this->service->someAnotherMethodYouHaveToCall();
}
public function authenticate(array $options)
{
$this->service->authenticateWithFacebookClass();
}
public function post($text, $url)
{
$this->service->postToFacebook($text);
}
}
By using Adapter pattern. We have adapter adaptee(Facebook service) for clients use.
As mentioned, do correct me if I am wrong.
Regards
Xu
hi @xu! thanks for the comment and sorry for the late answer… you’re right… this pattern is more like a factory than an adapter!
thank you for the contribution! and welcome to the blog.
Hi,
Thank you very much for great explanation and a solid working example. Beautiful !
Please write more on such patterns.
I have one query. What if different services require different no of parameters? For instance twitter requires text and url but say Facebook requires two more params. Should I use array to pass params? So Post class should know what params are required for each service?
HI Kiran!
Thank you for the comment! About your question, you can set a second parameter with null as default value. So if only have to do a simple if check.
Best regards!
got the understanding in the first go. brilliant job. Matter is simple and very well defined. Keep up the good work.
Thank you Anirudh!
Welcome to the blog!
Best regards.
Very well explained! Design patterns can be difficult to understand if not explain with a realistic and uncomplicated code sample. Keep up the good work mate.
Thank you Sumit!
It’s good to read good feedbacks.
Regards.
Good post and example. It’s sometimes hard to find examples for adapter that aren’t also suitable for factory pattern. This is a good case, because you can argue that the service adapter will be determined by user interaction. Thanks.
Thanks Arlo! Welcome to the blog!