The name of our today’s guest may suggest from the run what it is intended for. The adapter pattern, as it is referred to, is designed to convert the interface of a given class into the form of the interface expected by the client. This treatment allows the cooperation of classes that previously did not have the opportunity to work together.
Imagine the following situation. Marek, the owner of not so popular in Poland apple brand phone, goes to the house. On the spot it turns out that it is amazing, so decides to immortalize everything in photos. Unfortunately, he remembers that before leaving he forgot to charge his phone, which crosses his plans. The hero of the story desperately begins to ask other participants of the event for a charger. Unfortunately, the only one he manages to find has a micro USB connector incompatible with his phone. The situation seems hopeless, but Mark, taught by the mistakes of the past, is prepared for such an eventuality. It has a suitable adapter (Fig. 1). Similarly, the pattern discussed in the entry works.
The UML diagram of the adapter pattern looks like this:
If we would like to assign roles from the diagram to the objects in the above example, the target would be a power cable with a Lightning connector, an object with an adapted power cable with a micro USB connector, and an adapter – an adapter presented in Figure 1. 1.
Here’s how the code for this solution looks like. Let’s start by creating an AppleCharger interface and its LightningAdapter subclass:
AppleCharger public interface { public void chargeApplePhone(); }
public class LightningCharger implements AppleCharger { @Override public void chargeApplePhone() { System.out.println("I'm a charging iPhone."); } }
Next will be the interface of the adapted class AndroidCharger along with the microusbcharger subclass:
public interface AndroidCharger { public void chargeAndroidPhone(); }
public class MicroUsbCharger implements AndroidCharger{ @Override public void chargeAndroidPhone() { System.out.println("I'm a charging Android phone."); } }
To charge your phone, you need to use an adapter that will fulfill the role of an intermediary converting Mark’s requests. It has an implemented interface of the type to which it adapts and stores an instance of the adapted object:
public class MicroUsbChargerAdapter implements AppleCharger { MicroUsbCharger microUsbCharger; public MicroUsbChargerAdapter(MicroUsbCharger microUsbCharger) { this.microUsbCharger = microUsbCharger; } @Override public void chargeApplePhone() { microUsbCharger.chargeAndroidPhone(); System.out.println("Ykhm.. I mean, and charging iPhone."); } }
Finally, it remains to create a short test environment for our adapter. It contains a method that tests Applem phone chargers that downloads an AppleCharger object and calls its chargeApplePhone() method. As you can see, the use of the adapter made it possible to “press” the charger with a Micro USB connector, instead of the charger with the Lightning connector. This method will never know about this “twist”.
public class TestingPhoneCharging { public static void main(String[] args) { MicroUsbCharger microUsbCharger = new MicroUsbCharger(); LightningCharger lightningCharger = new LightningCharger(); AppleCharger microUsbChargerAdapter = new MicroUsbChargerAdapter(microUsbCharger); System.out.println("The Android charger says:"); microUsbCharger.chargeAndroidPhone(); System.out.println("nThe iPhone charger says:"); testIphoneCharing(lightningCharger); System.out.println("nThe microUsbChargerAdapter says:"); testIphoneCharing(microUsbChargerAdapter); } static void testIphoneCharing(AppleCharger appleCharger) { appleCharger.chargeApplePhone(); } }
The result of the code execution is as follows:
The Android charger says: I'm a Micro USB Charger. The iPhone charger says: I'm a Lightning charger. The microUsbChargerAdapter says: I'm a Micro USB Charger. Ykhm.. I mean, a Lightning charger.
In the example above, you will notice a method that tests chargers for Apple phones. It retrieves an AppleCharger object and calls its chargeApplePhone() method. As you can see, the use of the adapter made it possible to “press” the charger with a Micro USB connector, instead of the charger with the Lightning connector. This method will never know about this “twist”.
In conclusion, the adapter pattern can be used wherever a class with the wrong interface is required. Converts the interface to a customer-compliant form. Note, however, that the example from the entry was simple. Sometimes the target interface can be very complex, and then the implementation of the adapter can require a lot of work.
All the code from the entry is on my Githubie.
Sources: Eric Fre
[1]eman, Elisabeth Freeman, Kathy Sierra, Bert Bates: Design Patterns. Move your head!