Adapter Pattern vs. Bridge Pattern
Tags: webdev, programming, php, tutorial
Published: 2021-10-04 07:05:29

The Adapter pattern and the Bridge Pattern have brought along a lot of confusion. In this post we're going to look at what they are, what their differences are & where they might be similar.

## 🔌 Adapter Pattern
The Adapter Pattern tries to solve the problem of making two (or more) *incompatible* classes **compatible**, by using an intermediate class that implements a predefined interface.

*- Wait what? Let's try this again!*

### The problem
Imagine a single `Feed` that wants to display the latest topics from multiple sources, like: Reddit & Hacker News. For these sources we have two API clients: `RedditApi` and `HackerNewsApi`. Both return a list of topics, but their APIs are not the same.

```php
class RedditApi {
    public function fetchTopicItems(): RedditFeedIterator {
        // Returns a `RedditFeedIterator` that provides `Topic` objects, that hold a `title`, `date` and `url`.
    }
}

class HackerNewsApi {
    public function getTopics(): array {
        // returns an array of ['topic_title' => '...', 'topic_date' => '...', 'topic_url' => '...']
    }
}
```

We don't want to make our feed know about the different implementations, because we might want to add another source in the future and that would mean adding even more code to the feed. Instead, we'll apply the Adapter Pattern. 

### The solution
The Adapter Pattern consists of these 4 elements:

- 🙋 **Client:** This is the class that want's to connect to multiple sources. This would be `Feed` in our example.
- 📚 **Adaptee:** A source the *Client* wants to connect to. In our example we have two: `RedditApi` & `HackerNewsApi`.
- 🎯 **Target:** An interface or contract that defines a single API the *Client* will connect to.
- 🔌 **Adapter:** A class that implements the *Target* interface and delegates to an *Adaptee* source and formats its output.

First let's settle on a *Target* interface; we'll call it `TopicAdapterInterface` and it will have a `getTopics()` method that returns an `iterable` of topics, where every topic is an array with `title`, `date` and `url`. So it can be an array of arrays, or a Generator/Iterator of arrays.

*If you aren't familiar with Generators or Iterators, then please check out my [Generators over arrays](https://doeken.org/blog/generators-over-arrays) post.*

```php
interface TopicAdapterInterface
{
    /**
     * @return iterable Iterable of topic array ['title' => '...', 'date' => '...', 'url' => '...']
     */
    public function getTopics(): iterable;
}
```

Now we can create a `Feed` class that uses these adapters. We'll loop over every adapter, and `yield` their results, so we get a single continuous stream of topics as a `Generator`. This of course doesn't take a date into consideration, but it's enough for this example.

```php
class Feed
{
    /**
     * @param TopicAdapterInterface[] $adapters The adapters.
     */
    public function __construct(public array $adapters) {}

    public function getAllTopics(): iterable
    {
        foreach ($this->adapters as $adapter) {
            yield from $adapter->getTopics();
        }
    }
}
```

So we have a *Client* `Feed`, a *Target* `TopicAdapterInterface` and two *Adaptees* `RedditApi` & `HackerNewsApi`. That means that we are only missing two *Adapters*. We'll create these first, and then we'll look at what makes them tick.

To make it a bit easier working with the Iterators, I'll be using the `iterator_map()` function from my [`doekenorg/iterator-functions`](https://github.com/doekenorg/iterator-functions) package.

```php
class RedditTopicAdapter implements TopicAdapterInterface
{
    public function __construct(public RedditApi $reddit_api) {}

    public function getTopics(): iterable
    {
        return iterator_map(
            fn (Topic $topic) => [
                'title' => $topic->getTitle(),
                'date' => $topic->getDate('Y-m-d H:i:s'),
                'url' => $topic->getUrl(),
            ],
            $this->reddit_api->fetchTopicItems(),
        );
    }
}

class HackerNewsTopicAdapter implements TopicAdapterInterface
{
    public function __construct(public HackerNewsApi $hacker_news_api) {}

    public function getTopics(): iterable
    {
        return iterator_map(
            fn (array $topic) => [
                'title' => $topic['topic_title'],
                'date' => \DateTime::createFromFormat('H:i:s Y-m-d', $topic['topic_date'])->format('Y-m-d H:i:s'),
                'url' => $topic['topic_url'],
            ],
            $this->hacker_news_api->getTopics(),
        );
    }
}
```

Here you can see our two adapters: `RedditTopicAdapter` and `HackerNewsTopicAdapter`. Both of these classes implement the `TopicAdapterInterface` and provide the required `getTopics()` method. They each get their own *Adaptee* injected as a dependency, and use this to retrieve topics and format it to the required array.

This means that our `Feed` can now use these adapters by injecting them in its constructor. To connect this all together it could look a little something like this: 

```php
$hacker_news_adapter = new HackerNewsAdapter(new HackerNewsApi());
$reddit_adapter = new RedditTopicAdapter(new RedditApi());
$feed = new Feed([$hacker_news_adapter, $reddit_adapter]);

foreach ($feed->getAllTopics() as $topic) {
    var_dump($topic); // arrays of [`title`, `date` and `url`]
}
```

### Benefits of the Adapter Pattern
- 🔄 You can plug in an extra *Adapter* at a later time, without having to change he *Client* implementation.
- 🖖 Only the *Adapter* needs to know about the *Adaptee* which enforces separation of concerns.
- 🔬 The *Client* code is easily testable, because it only relies on a *Target* interface.
- 📦 When working with an IoC container you can usually get / tag all services with a specific interface, making it very easy to find and inject or auto-wire all *Adapters* into the *Client*.

### Real world examples
The Adapter Pattern is one of the most used patterns, because of its extendability. It can even be extended by other packages without the original packages having to change. Here are some real world examples of this.

#### Cache adapters
Most frameworks have a caching system that has a single API for working with it, while providing adapters for different implementations, like: redis, memcache or a filesystem cache. Laravel calls these adapters a `Store` and you can find these stores in [`illuminate/cache`](https://github.com/illuminate/cache). They provide the *Target* interface for such a store in the [`illuminate/contracts`](https://github.com/illuminate/contracts/blob/master/Cache/Store.php)
repository.

#### Filesystem adapters
Another common thing is to write data to files. Files that may be located somewhere else, like: an FTP server, a Dropbox folder or Google Drive. One of the most used packages for writing data to files is [`thephpleague/flysystem`](https://github.com/thephpleague/flysystem). This packages provides a [FilesystemAdapter](https://github.com/thephpleague/flysystem/blob/2.x/src/FilesystemAdapter.php) interface that can have specific implementations. And because of this *Target* interface, others can build 3rd-party packages that provide another Filesystem; like: [`spatie/flysystem-dropbox`](https://github.com/spatie/flysystem-dropbox) by Spatie.

## 🔀 Bridge Pattern
The Bridge Pattern is often confused with the Adapter Pattern, and with good reasons. Let's look at what problem this pattern tries to solve and how it is different from the Adapter Pattern.

### The problem
Let's say we have two editors: a `MarkdownEditor` and a `WysiwygEditor`. Both editors can read and format some file and update the source on that file. The `MarkdownEditor` obviously returns Markdown text, while the `WysiwygEditor` returns HTML.

```php
class WysiwygEditor
{
    public function __construct(public string $file_path) {}

    protected function format(): string
    {
        return '<h1>Source</h1>'; // The formatted source.
    }

    public function read(): string
    {
        return file_get_contents($this->file_path);
    }

    public function store(): void
    {
        file_put_contents($this->file_path, $this->format());
    }
}

class MarkdownEditor
{
    public function __construct(public string $file_path) {}

    protected function format(): string
    {
        return '# Source'; // The formatted source.
    }

    public function read(): string
    {
        return file_get_contents($this->file_path);
    }

    public function store(): void
    {
        file_put_contents($this->file_path, $this->format());
    }
}
```

At some point in time, we need a Markdown editor and a WYSIWYG editor that can read & store files on an FTP server. We could create a new editor that extends the `MarkdownEditor` or `WysiwygEditor` and overwrites the `read()` and `store()` method. However, this will likely introduce a lot of duplicate code between the two. Instead, we'll use the Bridge Pattern.
 
### The solution
The Bridge Pattern also consist of 4 elements:

- 🎨 **Abstraction:** An abstract base class that delegates some predefined functions to a *Implementor*. In our example this will be an `AbstractEditor`.
- 🧑‍🎨 **Refined Abstraction:** A specific implementation of the *Abstraction*. In our example this will be `MarkdownEditor` and `WysiwygEditor`.
- 🖌️ **Implementor:** An interface that the *Abstraction* uses for delegation. In our example this will be a `FileSystemInterface`
- 🖼️ **Concrete Implementor:** A specific implementation of the *Implementor* that actually does the work. In our example this will be `LocalFileSystem` and a `FtpFileSystem`.

It is at this point, I think one of the things that makes this pattern hard to grasp is this:

<div style="text-align: center;"><img style="width: 400px;" alt="There is no bridge" src="https://doeken.org/assets/img/there-is-no-bridge.jpg"></div>

Unlike the Adapter Pattern, where there is an actual *Adapter*; the Bridge Pattern does *not* have a *Bridge*. But no worries, we'll see the thing that makes this the *Bridge* soon enough!

#### Refactoring the code
Let's refactor our example code by implementing the Bridge Pattern. We'll start by extracting the *Abstraction* from our two Editors.

```php
abstract class AbstractEditor {
    public function __construct(public string $file_path) {}

    abstract protected function format(): string;

    public function read(): string
    {
        return file_get_contents($this->file_path);
    }

    public function store(): void
    {
        file_put_contents($this->file_path, $this->format());
    }
}

class WysiwygEditor extends AbstractEditor
{
    protected function format(): string
    {
        return '<h1>Source</h1>'; // The formatted source.
    }
}

class MarkdownEditor extends AbstractEditor
{
    protected function format(): string
    {
        return '# Source'; // The formatted source.
    }
}
```

In this refactoring we've created an `AbstractEditor` that now contains all the duplicate code there was between the editors, and made the editors extend this abstraction. This way the editors, or *Refined Abstractions*, are only focussing on what they do best: formatting the source of the file.

But remember, we still don't have a *Implementor* or a *Refined Implementor* and we really want to use multiple file systems. So let's create the *Implementor* and a `LocalFileSystem` as the first *Refined Implementor*. Then we'll update the `AbstractEditor` to use the *Implementor*.

```php
interface FilesystemInterface {
    public function read(string $file_path): string;

    public function store(string $file_path, string $file_contents): void;
}

class LocalFileSystem implements FilesystemInterface {
    public function read(string $file_path): string
    {
        return file_get_contents($file_path);
    }

    public function store(string $file_path, string $file_contents): void
    {
        file_put_contents($file_path, $file_contents);
    }
}

abstract class AbstractEditor {
    public function __construct(private FilesystemInterface $filesystem, private string $file_path) {}

    abstract protected function format(): string;

    public function read(): string
    {
        return $this->filesystem->read($this->file_path);
    }

    public function store(): void
    {
        $this->filesystem->store($this->file_path, $this->format());
    }
}
```

So here is the *"Bridge"*. It's the connection between the *Abstraction* and the *Implementor*. It connects one editor to one filesystem. But now the two can vary independently. We can add multiple editors that all have their own formatting, like `yaml`, `json` or `csv`. And all these editors can use any filesystem to read and store those files.

So now we can create a `FtpFileSystem` that reads and stores the formatted content on an FTP server.

```php
class FtpFileSystem implements FilesystemInterface {
    public function read(string $file_path): string
    {
        // Imagine the ultimate FTP file reading code here.
    }

    public function store(string $file_path, string $file_contents): void
    {
        // Imagine the ultimate FTP file writing code here.
    }
}
```

By using the Bridge Pattern we've made it possible to make 4 different implementation combinations:

```php
// 1. A local markdown file editor
new MardownEditor(new LocalFileSystem(), 'local-file.md')
// 2. An FTP markdown file editor
new MardownEditor(new FtpFileSystem(), 'ftp-file.md')
// 3. A local WYSIWYG file editor
new WysiwygEditor(new LocalFileSystem(), 'local-file.html')
// 4. An FTP WYSIWYG file editor
new WysiwygEditor(new FtpFileSystem(), 'ftp-file.html')
```

And if we were to add another `AbstractEditor` and another `FileSystem` we'd have 9 possible combination, while only adding 2 classes 🤯!

### Benefits of the Bridge Pattern
As we've seen there are some benefits to using the Bridge Pattern:

- 💧 The code is more DRY (Don't Repeat Yourself) by extracting the *Abstraction*.
- 🧱 It is more extendable by creating two separate abstractions that can vary independently.
- 🔬 The individual classes are smaller and therefore easier to test and understand.


### Similarities with Adapter Pattern
Another reason why some have trouble understanding the difference between the Bridge Pattern and the Adapter Pattern is that the connecting part of the "bridge" actually looks like an *Adapter*.

- A **Client** could be seen as the **Abstraction** as that also delegates to an interface.
- A **Target** could be seen as the **Implementor** as this also defines an interface to adhere to.
- An **Adapter** could be seen as the **Refined Implementor** because this implements the interface and fulfills the requirements.

That last one is probably the most confusing; as a *Refined Implementor* can actually *be* an *Adapter* to a dependency or *Adaptee*, but this is not required. The *Refined Implementor* will often be a class on its own, while an *Adapter* will always delegate. But the two are indeed not mutually exclusive.

## Thanks for reading

*I hope you enjoyed reading this article! If so, please leave a ❤️ or a 🦄 and consider subscribing! I write posts on PHP almost every week. You can also follow me on [twitter](https://twitter.com/intent/follow?screen_name=doekenorg) for more content and the occasional tip.*