Adapter Design Pattern

Adapter Design Pattern

Design patterns 101

ยท

9 min read

Hey again! If you haven't read my previous post about observer design pattern, you can catch up from here.

Okay. Today I am going to talk about another famous design pattern which developers usually meet. We can use this to smoothly integrate two or many components (I gave up the name, didn't I?).

The story

Now the Banana Republic has become a huge tourist attraction. So, they have opened a hotel called Banana Hotels and Resorts near their majestic beach (Breaking News: You probably did not see that coming ha? Banana Republic is an island). The rooms have electricity (Wow that is new) and the room walls have squared pin power outlets (Don't know whether they have a different name for that ๐Ÿ˜‘ I am not good with names). Something like below.

image.png

One day a tourist came in and complained to the management that he cannot charge his laptop because the laptop has a round pin power code. He was frustrated about the situation and asked for an immediate solution from the management. The management has agreed to provide a solution for him for the incident. But they had no clue what to do. They immediately called for a meeting and started discussing. After some arguments and a few broken jaws, they found a solution.

WHAT WAS THAT?

First they discussed about changing all the power outlets of that room to a rounded pin socket. One manager asked "What will happen if the next customer had a square pin power code? Replacing the power outlets again will be very costly". The suggestion immediately put down (Now you know who got the broken jaw ๐Ÿ˜‹). After a while, one of the junior managers came up with something that looked promising. He suggested to make an item which can work in between the power code and the power outlet. So the hotel does not need to change their power outlets and the tourist can use his same old round pin power cable. "What if the next tourist comes with a completely different power cable?", someone asked. "No problem. Then we can facilitate the same from our item. Just need a few modifications", the manager replied. Everyone agreed and the junior manager suggested to call that as THE Adapter. A simple yet a perfectly working solution.

I know what you are thinking ๐Ÿ™„

image.png

Let's try to understand the concept, shall we?

Blank diagram - Page 2 (2).png

In here, the wall socket is the target. It is what we are using without any issues right now. The foreign object or the external party (in this context, the power cable) we have no control of, is called an Adaptee. To connect or to make the interaction a success between the target and the adaptee, we create an Adapter.

Changing the target will make the situation difficult. Because the target is working perfectly at present. Who can guarantee that the replacing target will work the same as previous one? And again it will be very costly to change the target from time-to-time to facilitate the needs. Adaptee has a different story. It can be anything. We cannot force the adaptee to be something that the target likes, or something that the target completely supports without any hassle. Basically they both are kind of arrogant ends who do not like to change themselves. Hence,

09561c34-f54b-4cec-9b83-53951f03f75e_text.gif

But there is a thing you need to keep in your mind (pardon me if this situation I am trying to explain is wrong. I have no idea about the voltages ๐Ÿ˜Š But you should be able to understand the point I am getting at). Imagine your wall socket has a lower voltage and your device needs a significantly higher voltage than the wall socket. What you can do is, you can add a transformer in between your socket and the laptop cable. So, the transformer will increase the power and provide to the laptop. Like an adapter, correct? WRONG! In this situation, the transformer will not work as an adapter. Because, it alters the behavior of the target (by increasing the voltage). An adapter should never do that. In simple terms, it should blindly pass whatever it receives from one end, to the other end. It must not interfere with the data. Only thing it should do is, to facilitate a bridge between those two points.

We call the relationship between adapter and the adaptee as a "HAS-A" relationship. It is called composition (But in some cases as I read, this can be done through multiple inheritance too. Since I am a PHP geek, I don't have any experiences in multiple inheritance. So I'll be moving with the composition. Selfish? I don't know. Maybe. Enough talk. Hop hop). In here, we can say like "Multi plug adapter HAS A laptop charger". Even though it does not make a sense in grammatical universe, it makes perfect sense in the software engineering universe. There are many relationships when comes to software engineering such as HAS-A (composition) and IS-A (inheritance). It is a different topic for another day.

In future, if we have another type of laptop charger which has a completely different interface, we just have to modify our adapter. We do not have to touch the wall socket or the laptop charger. They can work perfectly in their own worlds. Without knowing any of this. How cool is that?

Simple yet awesome!

Just imagine this happens in the computer world. How would you solve it? Let's dig a little deeper into the situation.

  1. Obviously we need a wall socket and a laptop charger.
  2. We need a bridge or an adapter in between to connect those two.
  3. One side of that adapter needs to facilitate the needs of the wall socket. The other side needs to facilitate for the charger. In simple terms, one side needs to be plugged-in into the wall, while the other side should have a place to plug-in the laptop charger.

First let's see how the class diagram should be with an optimum solution.

Blank diagram - Page 2 (3).png

In above class diagram, target is an interface. The functions defined in the interface may or may not be compatible with the adaptee (I mean, who likes to take risks now days?). Hence, a separate concrete class (an adapter) implementing the interface is defined in between them to work as a bridge to connect themselves. Finally, the target knows where to call in the adapter, the adapter knows where to call in the adaptee. It is a win-win.

The intention of the adapter is not to add, modify or alter any behaviors of the adaptee. It's sole purpose is to blindly pass whatever it receives from the target to the adaptee and to make those two points inter-operable.

Let's jump into the code shall we?

(Before getting your hopes up, below code is based on PHP. It is almost same with other languages, only the syntaxes are different)

As discussed above with the class diagram, first we need an interface.

interface ConnectToPower {
    function connection();
}

This interface has the abstract function which needs to be implemented on the adapter class.

Let's create the adapter class implementing the above interface.

class PowerAdapter implements ConnectToPower {

    // @override
    function connection() {
        // Connect the Laptop charger through here.
    }
}

As you can see in above code, the interface ConnectToPower is overridden on the adapter class. All we know is we can initiate the bridge or the connection with the laptop charger from the connection() function. But, we do not know how to do that yet.

Where the hell is our laptop charger? Oh! There it is.

class LaptopCharger {
    function roundPinConnection() {
        echo "Round Pin Connected";
    }
}

Now, the laptop charger has a round pin connection. We haven't created our wall socket yet. But we know that it does only support the square pin plugs. Before creating the wall socket, we'll update the code on the adapter, since we have the details of our laptop charger now.

class PowerAdapter implements ConnectToPower {

    private $adaptee;

    function __construct(LaptopCharger $laptopCharger) {
        $this->adaptee = $laptopCharger;
    }

    // @override
    function connection() {
        $this->adaptee->roundPinConnection(); // Here we connect the adapter to a round pin plug.
    }
}

What have we done ๐Ÿ™„? Okay calm down. Let's break it down slowly.

Let's focus on what we already know. We know that the laptop charger has a roundPinConnection() and somehow we need to connect it from the connection() method that was overridden from the ConnectToPower interface. In order to get the data of the laptop charger, it needs to be injected to the adapter class. In that case, simply create a constructor to inject the LaptopCharger class and assign the injected object to a private variable. Why private? Because only the adapter should know the information. Then from the overridden method, we should be able to access the object (LaptopCharger) we assigned to the variable ($adaptee), and through there we access the roundPinConnection() function in the LaptopCharger class. Simply now we have connected the laptop charger to the adapter.

Finally, let's create the wall socket.

class WallSocket {

    function __construct() {
        $this->squarePinConnection();
    }

    function squarePinConnection() {
        $adapter = new PowerAdapter(new LaptopCharger);
        $adapter->connection();
    }
}

In here what we do is, we tell the wall socket's square pin, to plug-in the adapter which has the round pin laptop charger connected.

When you switch on,

new WallSocket;

WallSocket's squarePinConnection() will call the connection() function of the PowerAdapter and then, the PowerAdapter will call the roundPinConnection function on the LaptopCharger.

The result should get printed as Round Pin Connected.

THE END!

Final codebase looks like follows.

interface ConnectToPower {
    function connection();
}

class WallSocket {

    function __construct() {
        $this->squarePinConnection();
    }

    function squarePinConnection() {
        $adapter = new PowerAdapter(new LaptopCharger);
        $adapter->connection();
    }
}

class PowerAdapter implements ConnectToPower {

    private $adaptee;

    function __construct(LaptopCharger $laptopCharger) {
        $this->adaptee = $laptopCharger;
    }

    // @override
    function connection() {
        $this->adaptee->roundPinConnection();
    }
}

class LaptopCharger {
    function roundPinConnection() {
        echo "Round Pin Connected";
    }
}

new WallSocket;

Usage of this design pattern

Adapter pattern is used when your existing class is not compatible with the new class you are trying to integrate. So, the adapter class will work as a middle layer to do the communication between the existing class and the new class.

There are pros and cons when implementing a pattern like this. Major concern is the overall complexity of the code can get increased due to introducing new classes and interfaces. But however, this pattern follows two of the SOLID principles, single responsibility and open/closed (If you do not know what the SOLID principles are, do not worry. I'll bring a series of solid principles in future).

This pattern is called (as you have guessed already by now) Adapter Design Pattern.

Phew! Let's meet again soon with another episode of Simple yet awesome!

Till then,

bye-bye-bye.gif

ย