The Adapter Design Pattern allows objects with incompatible interfaces to work together by providing a wrapper or adapter class that acts as a bridge between them. It converts the interface of one class into another interface that clients expect.
// Adapter Interface
// Vehicle Interface
interface Vehicle {
void accelerate();
void brake();
}
// Car class implementing Vehicle interface
class Car implements Vehicle {
@Override
public void accelerate() {
System.out.println("Car: Accelerating");
}
@Override
public void brake() {
System.out.println("Car: Braking");
}
}
// Truck class implementing Vehicle interface
class Truck implements Vehicle {
@Override
public void accelerate() {
System.out.println("Truck: Accelerating");
}
@Override
public void brake() {
System.out.println("Truck: Braking");
}
}
// Adaptee Interface
// ToyCar Interface
interface RemoteControlToy {
void moveForward();
void stop();
}
// Adaptee
// ToyCar class implementing ToyCar interface
class ToyCar implements RemoteControlToy {
@Override
public void moveForward() {
System.out.println("ToyCar: Moving forward");
}
@Override
public void stop() {
System.out.println("ToyCar: Stopping");
}
}
// Concrete Adapter
// Adapter Class that adapts ToyCar to Vehicle interface
class ToyCarAdapter implements Vehicle {
private RemoteControlToy toyCar;
public ToyCarAdapter(RemoteControlToy toyCar) {
this.toyCar = toyCar;
}
@Override
public void accelerate() {
toyCar.moveForward();
}
@Override
public void brake() {
toyCar.stop();
}
}
public class Main {
public static void main(String[] args) {
// Create and use the Car object
Car car = new Car();
car.accelerate();
car.brake();
// Create and use the Truck object
Vehicle truck = new Truck();
truck.accelerate();
truck.brake();
// Create a ToyCar object
RemoteControlToy toyCar = new ToyCar();
// Create a ToyCarAdapter object, which acts as an adapter
Vehicle toyCarAdapter = new ToyCarAdapter(toyCar);
// Use the ToyCarAdapter object
toyCarAdapter.accelerate();
toyCarAdapter.brake();
}
}
In this example, we have the Vehicle
interface representing the common interface for vehicles, which includes methods like accelerate
and brake
. The Car
and Truck
class implements the Vehicle
interface and represents a real car with its own implementations of the methods.
The RemoteControlToy
interface represents a toy car and has its own methods moveForward
and stop
. The ToyCar
class implements the RemoteControlToy
interface and represents a specific type of toy car with its own implementations of the methods.
The ToyCarAdapter
class adapts the RemoteControlToy
interface to the Vehicle
interface. It takes a ToyCar
object in its constructor and provides implementations of the accelerate
and brake
methods by delegating the calls to the corresponding methods of the ToyCar
object.
Use the Adapter class when you want to use some existing class, but its interface isn’t compatible with the rest of your code.
- The Adapter pattern lets you create a middle-layer class that serves as a translator between your code and a legacy class, a 3rd-party class or any other class with a weird interface.
- Allows objects with incompatible interfaces, such as the
Vehicle
interface and theToyCar
interface, to work together by providing an adapter that converts the interface of one object to the interface expected by the client.
- Allows objects with incompatible interfaces, such as the
Use the pattern when you want to reuse several existing subclasses that lack some common functionality that can’t be added to the superclass.
- You could extend each subclass and put the missing functionality into new child classes. However, you’ll need to duplicate the code across all of these new classes, which smells really bad.
- The much more elegant solution would be to put the missing functionality into an adapter class. Then you would wrap objects with missing features inside the adapter, gaining needed features dynamically. For this to work, the target classes must have a common interface, and the adapter’s field should follow that interface. This approach looks very similar to the Decorator pattern.
For example:
Suppose we have an existing hierarchy of vehicle classes that implement the Vehicle
interface. The hierarchy includes classes such as Car
and Truck
each with their own specific implementations of the accelerate
and brake
methods.
Now, let's say we have a new requirement where we need to introduce a different type of vehicle called a ToyCar
, which represents a toy version of a car. The ToyCar
class implements the ToyCar
interface, which has methods like moveForward
and stop
. However, we also want to treat the ToyCar
as a Vehicle
and be able to use it in our existing code that expects objects of type Vehicle
. But since ToyCar
does not implement the Vehicle
interface directly, we can't use it seamlessly with the existing codebase.
To solve this problem, we can introduce an adapter, which in this case is the ToyCarAdapter
class. The ToyCarAdapter
class implements the Vehicle
interface and internally uses a ToyCar
object. It acts as a bridge between the ToyCar
object and the Vehicle
interface.
By providing the ToyCarAdapter
class, we can now reuse the existing code that works with Vehicle
objects and use the ToyCar
as if it were a Vehicle
. The adapter converts the accelerate
and brake
method calls made on the Vehicle
interface to corresponding calls on the ToyCar
object's moveForward
and stop
methods.
This way, we can achieve the desired functionality of treating the ToyCar
as a Vehicle
without modifying the existing Vehicle
hierarchy or the ToyCar
class itself. The Adapter Design Pattern allows us to reuse the existing subclasses (Car
, Truck
) and bridge the gap between the ToyCar
class and the expected Vehicle
interface, thereby adding the missing common functionality to the ToyCar
class.
The Adapter Design Pattern is useful when we want to reuse existing subclasses (like ToyCar
) that lack some common functionality (like implementing the Vehicle
interface) and can't be modified directly. The adapter acts as a bridge, allowing these incompatible classes to work together seamlessly.