Skip to content

Latest commit

 

History

History
134 lines (103 loc) · 6.61 KB

2A. Adapter.md

File metadata and controls

134 lines (103 loc) · 6.61 KB

Adapter

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.

image

// 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.

Adapter Design Pattern Summary

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 the ToyCar interface, to work together by providing an adapter that converts the interface of one object to the interface expected by the client.

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.