Adapter Design Pattern Also known as Wrapper.

Adapter Design Pattern Also known as Wrapper.

Problem
Imagine you're developing a fitness tracking application that gathers workout data from various fitness devices in XML format and generates detailed performance summaries for users.As you strive to enhance the app's capabilities, you contemplate integrating a sophisticated machine learning algorithm for personalised workout recommendations.
However, there's a snag: the algorithm exclusively processes data in JSON format. Adapting the algorithm to accommodate XML data might disrupt existing code that relies on its current functionality. Additionally, accessing and modifying the algorithm's source code might not be feasible, rendering this solution impractical.

Solution
To bridge the gap between the CSV data format used by fitness devices and the JSON format required by the advanced machine learning algorithm, you can implement an adapter. This adapter serves as a mediator, facilitating communication between the two objects with different interfaces.

The adapter encapsulates the conversion process, shielding the underlying complexity from both the fitness tracking application and the machine learning algorithm. For instance, you can design an adapter that transforms workout data from CSV format to JSON format seamlessly.

Adapters not only facilitate data conversion but also enable collaboration between objects with disparate interfaces. Here's how it operates within our scenario:

  1. The adapter defines an interface that aligns with the requirements of either the fitness tracking application or the machine learning algorithm.

  2. Utilising this interface, the fitness tracking application or the algorithm can interact with the adapter's methods securely.

  3. Upon receiving a request, the adapter translates the data from CSV to JSON format (or vice versa) and forwards it to the respective recipient, ensuring compatibility.

  4. In some cases, you can implement a two-way adapter capable of facilitating bidirectional data conversion, enabling seamless communication between the fitness tracking application and the machine learning algorithm.

    Real Life Example :
    When embarking on your first trip from the US to Europe, you might encounter an unexpected challenge when attempting to charge your laptop. The varying power plug and socket standards across countries can catch you off guard. This discrepancy means that your US plug won't fit into a German socket, for example. Thankfully, this issue can be resolved by employing a power plug adapter equipped with both an American-style socket and a European-style plug.

Object adapter

This implementation uses the object composition principle: the adapter implements the interface of one object and wraps the other one. It can be implemented in all popular programming languages.

// Target interface
interface EuropeanSocket {
    fun plugIn(type: String)
}

// Adaptee (adapting the US socket)
class AmericanSocket {
    fun plugIn(type: String) {
        println("Plugged into American socket: $type")
    }
}

// Adapter
class SocketAdapter(private val americanSocket: AmericanSocket) : EuropeanSocket {
    override fun plugIn(type: String) {
        println("Using adapter to plug into European socket")
        americanSocket.plugIn(type)
    }
}

// Client
class Laptop {
    fun charge(europeanSocket: EuropeanSocket, type: String) {
        europeanSocket.plugIn(type)
    }
}

fun main() {
    val americanSocket = AmericanSocket()
    val socketAdapter = SocketAdapter(americanSocket)
    val laptop = Laptop()

    laptop.charge(socketAdapter, "Type C")
}
  • EuropeanSocket is the target interface that defines the method plugIn(type: String).

  • AmericanSocket is the adaptee, representing the existing American socket implementation.

  • SocketAdapter is the adapter class that implements the EuropeanSocket interface. It contains a reference to the AmericanSocket and delegates the plugIn call to it.

  • Laptop is the client class that consumes the EuropeanSocket interface. It has a method charge which accepts an object that implements EuropeanSocket.

  • In the main function, an instance of AmericanSocket is created, then an instance of SocketAdapter is created, passing the AmericanSocket instance to it. Finally, the Laptop charges using the SocketAdapter, which internally uses the AmericanSocket to plug into the European socket.