Adapter Design Pattern
Adapter design pattern is one of the classic Gang of Four structural design patterns. As the name suggests, we use Adapter Design pattern to make two unrelated interfaces work together.
Adapter design pattern is a lifesaver when an incompatible module needs to be integrated with an existing module, making no source code modifications.
An Adapter pattern acts as a connector between two incompatible interfaces that otherwise cannot be connected directly. An Adapter wraps an existing class with a new interface so it becomes compatible with the client’s interface.
The object that joins the unrelated interfaces is an Adapter, while the original interface is an Adaptee.
As a real life example, we can think of a mobile charger as an adapter because mobile battery needs 3 volts to charge but the normal socket produces either 120V – 240 Volts. Therefore, the mobile charger works as an adapter between the mobile charging socket and the wall socket.
This pattern is also called Wrapper because it wraps the existing object.
When to us Adapter design Pattern?
We have an existing object that provides the functionality required by the client, but client code cannot us the object because it expects the functionality in a different interface.
Adapter Design Pattern Implementation
There are two ways to implement Adapter Design Pattern.
Class Adapter Implementation
Object Adapter Implementation
- Adapter must implement the interface expected by the client.
- In Class Adapter Implementation, the Adapter method will call the expected functionality method of the inherited adaptee class.
- In Object Adapter Implementation, the Adapter will have an instance of adaptee class. The adapter method will call the expected functionality method of the inherited adaptee class.
Adapter Design Pattern Real World Example
Consider a scenario where the client is running an oil import business. The Client system is designed to work with the imperial system, which only understand Oil quantity in Gallons. The client wants the functionality to purchase oil from the traders.
We already have a similar functionality implemented which can allow client to purchase oil from traders but the functionality accepts metric system i.e. oil quantity in litres.
To tackle this issue, we will use Adapter Design Pattern that will provide client expected functionality with works with the imperial system.
Let’s implement all the classes that are required for this scenario.
Oil
Oil
class is a functionality class that calculates the price and performs the transaction.
package com.adevguide.java.designpatterns.adapter; public class Oil { private double oilPrice; public Oil(double quantityInLitre) { this.oilPrice = quantityInLitre * 2; // price is 2 dollars per litre System.out.println("Total Cost of purchase is " + oilPrice + " dollars. Purchase Complete."); } }
OilInLitreInterface
OilInLitreInterface is an adaptee interface. This defines the definition of actual logic class.
package com.adevguide.java.designpatterns.adapter; public interface OilInLitreInterface { public Oil buyOil(double quantityInLitre); }
OilInLitre
OilInLitre is an implementation of adaptee interface. This class defines implemenation of actual logic.
package com.adevguide.java.designpatterns.adapter; public class OilInLitre implements OilInLitreInterface { public Oil buyOil(double quantityInLitre) { System.out.println("Purchasing " + quantityInLitre + " litres of Oil"); return new Oil(quantityInLitre); } }
OilInGallonInterface
OilInGallonInterface is a target interface. This is the interface that the client wants the target object.
package com.adevguide.java.designpatterns.adapter; public interface OilInGallonInterface { public Oil buyOilInGallon(double quantityinGallon); }
OilInGallonImplClass – Adapter Class Interface
OilInGallonImplClass
is an adpater class which extends adaptee implementation class OilInLitre
and inherit target interface OilInGallonInterface
.
package com.adevguide.java.designpatterns.adapter; public class OilInGallonImplClass extends OilInLitre implements OilInGallonInterface { public Oil buyOilInGallon(double quantityinGallon) { double quantityInLitres = convertGallonToLitre(quantityinGallon); return buyOil(quantityInLitres); } private double convertGallonToLitre(double gallonQuantity) { return gallonQuantity * 3.78541; } }
buyOilInGallon(double quantityinGallon)
is a target interface object which client will call. This method accepts the oil quantity in gallons.
Quantity is converted into litre using conversion logic convertGallonToLitre(double gallonQuantity)
and then adaptee class logic method buyOil(quantityInLitres)
is called to perform the transaction.
OilInGallonImplObject – Adapter Object Implemenation (Preferred Way)
OilInGallonImplObject is an adapter class that implements target interface OilInGallonInterface.
package com.adevguide.java.designpatterns.adapter; public class OilInGallonImplObject implements OilInGallonInterface { private OilInLitreInterface oilInLitre = new OilInLitre(); public Oil buyOilInGallon(double quantityinGallon) { double quantityInLitres = convertGallonToLitre(quantityinGallon); return oilInLitre.buyOil(quantityInLitres); } private double convertGallonToLitre(double gallonQuantity) { return gallonQuantity * 3.78541; } }
An instance of adapee class OilInLitreInterface
is created using composition principle. buyOilInGallon(double quantityinGallon)
is a target interface object which client will call. This method accepts the oil quantity in gallons.
Quantity is converted into litre using conversion logic convertGallonToLitre(double gallonQuantity)
and then adaptee class instance oilInLitre
method buyOil(quantityInLitres)
is called to perform the transaction.
Client
Client
will act as a client class to show the scenario.
package com.adevguide.java.designpatterns.adapter; public class Client { public static void main(String[] args) { System.out.println("Adapter Class Implementation"); OilInGallonInterface adapterInterfaceClass = new OilInGallonImplClass(); adapterInterfaceClass.buyOilInGallon(1); System.out.println("***********************************************************"); adapterInterfaceClass.buyOilInGallon(10); System.out.println("***********************************************************"); System.out.println("Adapter Object Implementation"); OilInGallonInterface adapterInterfaceObject = new OilInGallonImplObject(); adapterInterfaceObject.buyOilInGallon(1); System.out.println("***********************************************************"); adapterInterfaceObject.buyOilInGallon(40); } }
Output
Adapter Class Implementation Purchasing 3.78541 litres of Oil Total Cost of purchase is 7.57082 dollars. Purchase Complete. *********************************************************** Purchasing 37.8541 litres of Oil Total Cost of purchase is 75.7082 dollars. Purchase Complete. *********************************************************** Adapter Object Implementation Purchasing 3.78541 litres of Oil Total Cost of purchase is 7.57082 dollars. Purchase Complete. *********************************************************** Purchasing 151.4164 litres of Oil Total Cost of purchase is 302.8328 dollars. Purchase Complete.
We have created Object OilInGallonImplClass()
OilInGallonImplObject()
of target interface OilInGallonInterface
.
buyOilInGallon()
accepts quantity in gallons, converts it to litres and performs the purchase.
Pitfalls of Adapter Design Pattern
Adapter Design Pattern implemented using Adapter Class Implementation is not recommended. It creates an object that exposes unrelated methods in the code, populating it.
Example of Adapter Design Pattern
- java.util.Arrays#asList()
- java.io.InputStreamReader(InputStream) (returns a Reader)
- java.io.OutputStreamWriter(OutputStream) (returns a Writer)
Adapter Design Pattern Source Code
The entire source code used in this tutorial is available at our GitHub Repository.
References
baeldung.com/java-adapter-pattern
medium.com/@ssaurel/implement-the-adapter-design-pattern-in-java-f9adb6a8828f
Nice explanation of Adapter pattern