Hi guys! Today I’ll talk about the Adapter Pattern.
The adapter pattern is a Structural Design Pattern that allows objects to work together although they have incompatible interfaces.
This pattern is really handy in real life. The reason is: many times, classes need to collaborate but they can’t because they have different interfaces.
Let me show you a real adapter:
In this picture you can show:
- European wall Outlet
- Adapter USA – European
- USA Plug
You imagine this situation: You are in Spain and need to work with your laptop but you can’t charge it because you can’t connect your laptop charger 🙁 . It uses a USA Plug! you need to adapt your plug to the European standard or in other words, you need an ADAPTER.
This is a concrete example about this Pattern because you have a CLIENT class (European wall Outlet) that needs to collaborate with Adaptee (USA Plug) and for this reason, you need to use Adapter (Adapter USA – European).
In resume, you have a useful class that you want to use it in your application but these classes have incompatible interfaces. You wouldn’t like to modify these classes because these have a lot of business rules and these are really complex. For this reason, you can use the adapter pattern! You need to wrap a class in another Adapter class with a compatible interface! That’s all! Right now both classes can work together!
HR System 😉
We work in an HR startup company. The recruiter team have lots of experience and we need a system to centralize their knowledge!
The recruiter told me “Franco, we will buy a Human Resource Data Base to CoderZ (a website where to have a lots CV’s ), we need to create a system to exploit this information and if we have success, in the future we will buy other databases“.
Ok, I called CoderZ tech leader to ask him about the database format because I needed to design my system. They told me: “It’s easy we give you the information in XML format”.
Finally, I designed the system and HR team helped me to create a GREAT SECRET ALGORITHM to identify the best candidates!
Our system is simple! We have a JobHunt class that has all the information about the search!
Then we have HrAnalyser. This class receives a JobHunt and gets the best candidates! HrAnalyser applies our secret algorithm!
HrAnalyser class has a collection of IHrDataSource, in other words, the analyser has a collection the all our Database Providers. At the moment, we have just one: CoderZ.
Each provider must implement the interface IHrDataSource. When we need to find the best candidates the HrAnalyserService instantiates all provider and ask them: “Ey, you get me your candidates for this job hunt!“
This focus, It’s really similar to Factory pattern
- Create our JobHunt ! (We need a lot of developers!)
- Create our Hr analyser and pass it the JobHunt!
- The analyser instantiates all Providers that implement IHrDataSource! And It adds each of them to the internal collection: hrDataSources. At the moment, it just instantiates CoderZService!
- The analyser applies a lot of magic and returns us the best candidates!
For 1 year, the company worked like a champ ;).
Every month the CoderZ company sends us the information and we analyse it! Our algorithm works great! 😀
Finally, the CEO call me and told me “Fran, we need to add two providers more quickly!“
Great problem 🙁 but we have a good base code! 😉
HR System + Adapter Pattern!
Ok, we need to integrate two providers more:
- LinkedIn: they will give us a SDK.
- Universo8Bit: they have an API to send requests!
We don’t want to modify our code! Our secret algorithm works great and CoderZ provider works well too!
But we can’t use the SDK directly in our HrAnalyserService because we can interact only with providers that implement IHrDataSource interface.
Our solution is to implement the Adapter Pattern! We need to adapt the new providers for these can work with our HrAnalyserService .
- Client: is the class that contains the business logic. You don’t want to modify this class because it is a complex class however you want that client and adaptee work together but they have different interfaces!
- Adapter (Interface): other classes must implement it to interact with the Client! The client only works with classes that implement this interface!
- ConcreteAdapter: It is a wrapper! Your main responsibility is to interact between Client and Adaptee class. This class wrap the “adaptee” in a private variable. Then the client sends requests to ConcreteAdapter and finally, it redirects the requests to its adaptee variable!
- Concrete adapter interacts with the adaptee and the client interacts with the Adapter!
- Adaptee: It’s a handy class (maybe 3rd-party, dll, etc).The client can’t interact with it directly because it has an incompatible interface
We’ll refactor our code to implement Adapter pattern!
First! We need to create the adapter (wrapper) to interact with LinkedIn SDK! The SDK has a Manager class that we need to use to send requests to LinkedIn server!
Second! We need to create the adapter Universo8bit who interacts with the Universo8bit API!
Every provider needs to implement IHrDataSource because our HrAnalyserService only can interact with this kind of classes!
In other words, the adapter class wraps the adaptee and allows that the client can interact with it!
In the picture you can see how:
- We instantiate a JobHunt
- We instantiate a analyser!
- Finally, we ask to the analyser: “Ey, Get me the best candidates from this search!”
We don’t change anything! But where is the magic? 😮
Maybe in two places:
FIRST: When we instantiate an Analyser, the class automatically loads their data sources (providers). The method LoadDataSources() (it is in the Analyser Constructor) uses reflection to search classes that implement IHrDataSource and finally, instantiate them and add them to a private collection!
In other words, when we add a new Provider that implements IHrDataSource interface, the method LoadDataSource() automatically add this new provider to the internal collection.
This focus is really similar to the Factory Pattern!
SECOND: Adapter pattern! In the picture, you can see how the class LinkedInAdapterService wraps the adaptee (LinkedInManager) because the last one doesn’t implement the IHrDataSource interface and for this reason, it can’t interact we the Analyser directly!
(*) Every provider must implement Adapter pattern if they don’t implement the common interface!
Another way to fix this problem can be: to modify our Analyser and code a lot of switch statements! But obviously, that is NOT recommendable!
The adapter pattern is great and really useful when we interact with existing systems and we can’t modify them!