Design Patterns – Adapter

This Structural Design Pattern is used to convert an interface of a class to a different form, this allows that some different classes could work together. It ‘s the perfect approach to be used with Third Parties objects.

An adapter allows you to translate one interface for use with another. Adapters could be implemented using Composition or Inheritance, in our example, we’re using composition.


Intent

Convert the interface of a class into another interface clients expect. The adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.


Diagram

Adapter Design Pattern
Adapter Design Pattern Diagram

Client – collaborates with objects conforming to the Target interface.
Target – defines the domain-specific interface that Client uses.
Adapter – adapts the interface Adaptee to the Target interface.
Adaptee – defines an existing interface that needs adapting.


Some real world example

I had to create a billing process that could consume two external services to bill our clients, to do that I used Adapter as an Anti-Corruption Layer, it made our system more flexible and with this approach we can use other Libraries inside our Domain. However, this example could be confused with a Strategy Pattern and I will be doing a different approach to explain how it works 🙂

Think that we have such a small difference between Adapter and Strategy, adapter doesn’t have any behaviours inside its classes, it means, we just will call other classes to do something using our own interface.


Our problem and solution

To keep it more simple possible, I’ve made some search on E-bay or SuperMarket websites for adapters. (Genius, no? Lol). Well, the concept is exactly the same. Let’s see some adapters below

Based on this, I decided to make a Notebook USB-C Port example, it will allow our Monitor, Smartphone and Wifi to be plugged in this port. Keep it simple as it should be, look to your notebook, imagine these adapters and let’s implement it.

First let’s create right folders and files in our project, and it should be like this.

.designpatterns
.. src
... Structural
.... Adapter
..... Notebook
...... Adaptee
....... Monitor.php
....... Smartphone.php
....... Wifi.php
...... Adapters
....... MonitorAdapter.php
....... SmartphoneAdapter.php
....... WifiAdapter.php
...... Contracts
....... USBC.php
...... Notebook.php
.. tests
... Structural
.... Adapter
..... Notebook
...... NotebookTest.php

Let’s code

Let’s start with our “Contract”, USBC interface, because it will be used by our Notebook (Client) and also should be used by our Adapters.

USBC.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\Adapter\Notebook\Contracts;

interface USBC
{
    public function execute(): string;
}

Now we can create our client, our Notebook that will have our port method, that consumes our contract/interface.

Notebook.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\Adapter\Notebook;

use DesignPatterns\Structural\Adapter\Notebook\Contracts\USBC;

class Notebook
{
    public function port(USBC $usbc): string
    {
        return $usbc->execute();
    }
}

Now, lets implement some random classes (adaptees) that will be used by our Notebook. Also note that we used different names for our methods, to create a requirement to use an adapter. Imagine it as some thirdparty classes.

Monitor.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\Adapter\Notebook\Adaptee;

class Monitor
{
    public function display(): string
    {
        return "Monitor connected.";
    }
}

Smartphone.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\Adapter\Notebook\Adaptee;

class Smartphone
{
    public function start(): string
    {
        return "Smartphone connected.";
    }
}

Wifi.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\Adapter\Notebook\Adaptee;

class Wifi
{
    public function connect(): string
    {
        return "WIFI connected.";
    }
}

Now you have noticed that it is impossible to use our adaptees without an adapter, it means, how you will plug a monitor into our notebook port (USBC)? Here will come our adapters.


MonitorAdapter.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\Adapter\Notebook\Adapters;

use DesignPatterns\Structural\Adapter\Notebook\Adaptee\Monitor;
use DesignPatterns\Structural\Adapter\Notebook\Contracts\USBC;

class MonitorAdapter implements USBC
{
    private Monitor $monitor;

    public function __construct(Monitor $monitor)
    {
        $this->monitor = $monitor;
    }

    public function execute(): string
    {
        return $this->monitor->display();
    }
}

SmartphoneAdapter.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\Adapter\Notebook\Adapters;

use DesignPatterns\Structural\Adapter\Notebook\Adaptee\Smartphone;
use DesignPatterns\Structural\Adapter\Notebook\Contracts\USBC;

class SmartphoneAdapter implements USBC
{
    private Smartphone $smartphone;

    public function __construct(Smartphone $smartphone)
    {
        $this->smartphone = $smartphone;
    }

    public function execute(): string
    {
        return $this->smartphone->start();
    }
}

WifiAdapter.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\Adapter\Notebook\Adapters;

use DesignPatterns\Structural\Adapter\Notebook\Adaptee\Wifi;
use DesignPatterns\Structural\Adapter\Notebook\Contracts\USBC;

class WifiAdapter implements USBC
{
    private Wifi $wifi;

    public function __construct(Wifi $wifi)
    {
        $this->wifi = $wifi;
    }

    public function execute(): string
    {
        return $this->wifi->connect();
    }
}

Before finishing this we need to create our test to make sure that everything is working as expected.

NotebookTest.php

<?php

declare(strict_types=1);

namespace Tests\Structural\Adapter\Notebook;

use DesignPatterns\Structural\Adapter\Notebook\Adaptee\Monitor;
use DesignPatterns\Structural\Adapter\Notebook\Adaptee\Smartphone;
use DesignPatterns\Structural\Adapter\Notebook\Adaptee\Wifi;
use DesignPatterns\Structural\Adapter\Notebook\Adapters\MonitorAdapter;
use DesignPatterns\Structural\Adapter\Notebook\Adapters\SmartphoneAdapter;
use DesignPatterns\Structural\Adapter\Notebook\Adapters\WifiAdapter;
use DesignPatterns\Structural\Adapter\Notebook\Notebook;
use PHPUnit\Framework\TestCase;

class NotebookTest extends TestCase
{
    protected Notebook $notebook;

    protected function setUp(): void
    {
        parent::setUp();
        $this->notebook = new Notebook();
    }

    public function testItCanUseMonitor()
    {
        $result = $this->notebook->port(
            new MonitorAdapter(
                new Monitor()
            )
        );

        $this->assertEquals("Monitor connected.", $result);
    }

    public function testItCanUseSmartphone()
    {
        $result = $this->notebook->port(
            new SmartphoneAdapter(
                new Smartphone()
            )
        );

        $this->assertEquals("Smartphone connected.", $result);
    }

    public function testItCanUseWifi()
    {
        $result = $this->notebook->port(
            new WifiAdapter(
                new Wifi()
            )
        );

        $this->assertEquals("WIFI connected.", $result);
    }
}

You can follow our repository https://github.com/diegosm/designpatterns, everything is there.

I hope you understood this concept and feel free to ask something or do your contribution. There are many examples on the internet of this pattern.

This article is a part of our Design Pattern Series, follow our Design Patterns – What, when, why? link 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *




Enter Captcha Here :