Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Bridging Incompatible Interfaces with the Adapter Pattern

Tech 1

Structural Compatibility

The Adapter pattern serves as a bridge between two incompatible interfaces. This structural pattern allows classes with incompatible interfaces to collaborate without modifying their source code. It wraps an existing class (the adaptee) with a new interface (the target) that a client expects. This is particularly useful during system maintenance or integration when third-party libraries or legacy modules must be incorporated into a new architecture.

Practical Demonstration

Consider a presentation setup where an OldProjector requires a VGAInterface. The implementation of the legacy projector and its specific connector is shown below:

// Legacy Target Interface
interface VGAConnector {
    void connectViaVGA(String signal);
}

// Legacy Projector Implementation
class LegacyProjector implements VGAConnector {
    @Override
    public void connectViaVGA(String signal) {
        System.out.println("Legacy Projector receiving VGA signal: " + signal);
    }
}

A modern laptop typically outputs video via an HDMIInterface. The implementation for the modern output standard differs from the legacy input:

// Modern Adaptee Interface
interface HDMIConnector {
    void streamViaHDMI(String data);
}

// Modern Laptop Implementation
class ModernLaptop implements HDMIConnector {
    @Override
    public void streamViaHDMI(String data) {
        System.out.println("Modern Laptop streaming HDMI data: " + data);
    }
}

Attempting to connect the ModernLaptop directly to the LegacyProjector results in a type mismatch. To resolve this, we introduce an adapter that translates HDMI calls to VGA calls.

Class Adapter Implementation

The class adapter approach uses inheritance to achieve compatibility. The adapter inherits from the modern device (adaptee) and implements the legacy interface (target).

// Class Adapter
class HDMItoVGAClassAdapter extends ModernLaptop implements VGAConnector {
    @Override
    public void connectViaVGA(String signal) {
        System.out.println("[Class Adapter] Converting signal...");
        // The adapter translates the specific VGA connection request to the HDMI streaming method
        streamViaHDMI(signal);
    }
}

Object Adapter Implementation

Alternatively, the object adapter uses composition. This is generally preferred as it adheres to the principle of favoring composition over inheritance. The adapter holds a reference to the adaptee.

// Object Adapter
class HDMItoVGAObjectAdapter implements VGAConnector {
    private HDMIConnector modernDevice;

    public HDMItoVGAObjectAdapter(HDMIConnector device) {
        this.modernDevice = device;
    }

    @Override
    public void connectViaVGA(String signal) {
        System.out.println("[Object Adapter] Translating protocol...");
        modernDevice.streamViaHDMI(signal);
    }
}

Two-Way Adapter

In scenarios where interoperability is required in both directions (e.g., connecting a legacy device to a modern screen), a two-way adapter can be implemented. This adapter implements both interfaces and manages references to both objects.

class BidirectionalAdapter implements VGAConnector, HDMIConnector {
    private VGAConnector vgaDevice;
    private HDMIConnector hdmiDevice;

    public BidirectionalAdapter(VGAConnector vga) {
        this.vgaDevice = vga;
    }

    public BidirectionalAdapter(HDMIConnector hdmi) {
        this.hdmiDevice = hdmi;
    }

    @Override
    public void connectViaVGA(String signal) {
        System.out.println("[Bidirectional Adapter] HDMI to VGA");
        if (hdmiDevice != null) {
            hdmiDevice.streamViaHDMI(signal);
        }
    }

    @Override
    public void streamViaHDMI(String data) {
        System.out.println("[Bidirectional Adapter] VGA to HDMI");
        if (vgaDevice != null) {
            vgaDevice.connectViaVGA(data);
        }
    }
}

Usage and Implementation Details

The following demonstrates how the object adapter resolves the incompatibility issue in the main execution flow:

public class PresentationSetup {
    public static void main(String[] args) {
        LegacyProjector oldProjector = new LegacyProjector();
        ModernLaptop newLaptop = new ModernLaptop();

        // Direct connection fails due to interface mismatch
        // oldProjector.connectViaVGA(newLaptop); // Compile error

        // Using Object Adapter to bridge the gap
        HDMItoVGAObjectAdapter adapter = new HDMItoVGAObjectAdapter(newLaptop);
        oldProjector.connectViaVGA("Presentation Data");
        // Output via adapter triggers the HDMI stream
        adapter.connectViaVGA("Presentation Data");
    }
}

The primary distinction between class and object adapters lies in their flexibility. Object adapters allow the adaptation of multiple subclasses, whereas class adapters are restricted to a single specific adaptee due to the limitations of single inheritance in languages like Java. The two-way adapter extends the object adapter concept to support dual-directional translation, useful in complex integration scenarios involving heterogeneous systems.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.