Adapterパターンを使うメリットとPHP実装例

※この投稿は[Java言語で学ぶデザインパターン入門](https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797327030)を読んだ感想と復習を兼ねたまとめです

### Adapterパターンとは

Adapterパターンはデザインパターンのひとつで、既存のクラスを修正することなく、インタフェースを変更するための手法です。

[Java言語で学ぶデザインパターン入門](https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797327030)では、電化製品のACアダプタを例に説明されていました。家庭用のコンセントは100ボルトで固定されていますが、ノートパソコンが必要としているのは12ボルトの電源です。この「既に提供されているもの」と「いま必要としているもの」の差を吸収するのがAdapterの役割です。

Adapterパターンに登場する役割は、それぞれ次のように呼ばれるようです。

| 役割    | 意味                                     | 例                                                 |
| ------- | ---------------------------------------- | -------------------------------------------------- |
| Target  | いま必要としているもの（インタフェース） | ノートパソコンを動かすための12ボルトの電源         |
| Adapter | TargetとAdapteeの差を埋めるもの          | ノートパソコンと家庭用コンセントをつなぐACアダプタ |
| Adaptee | 既に提供されているもの                   | 家庭用コンセントの100ボルトの電源                  |

この関係をクラス図で表現すると次のようになります。


<img width="800" alt="スクリーンショット 2020-08-06 9.44.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500854/f1faf32f-0b50-9f78-75cc-818036c306ce.png">



また、AdapterパターンはWrapperパターンと呼ばれることもあるようです。

### 継承を利用したAdapterパターン実装例

前提として、次のようなBannerクラスが既に実装されているとします。

```php
class Banner
{
    private $string;

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

    public function showWithParen(): void
    {
        echo '(' . $this->string . ')' . "\n";
    }

    public function showWithAster(): void
    {
        echo '*' . $this->string . '*' . "\n";
    }
}
```

Bannerは、文字列を出力する方法を2パターン（`showWithParen()`と`showWithAster()`）持っています。

しかし、いま必要とされているのは次のようなインタフェースの実装です。

```php
interface PrintInterface
{
    public function printWeak(): void;
    public function printStrong(): void;
}
```

このPrintInterfaceを経由して、Bannerが持っているメソッドを呼び出す方法を考えます。

Adapterパターンには2つの実現方法がありますが、さきに紹介するのは継承を利用した方法です。

次のようなAdapterを作成します。

```php
class PrintBanner extends Banner implements PrintInterface
{
    public function printWeak(): void
    {
        $this->showWithParen();
    }

    public function printStrong(): void
    {
        $this->showWithAster();
    }
}
```

上記のPrintBannerは、既に実装されているBannerを継承して、いま必要とされているPrintInterfaceを実装しています。このようなAdapterを間にはさむことによって、既存のクラスを変更することなく、呼び出し元はインタフェース経由で目的の関数を呼び出すことが可能になります。

### 委譲を利用したAdapterパターン実装例

Adapterパターンを実現するもうひとつの方法は、委譲を利用する方法です。

委譲という言葉は知りませんでしたが、利用したいクラスのインスタンスを生成し、そのインスタンスを経由して目的の処理を実行することを委譲というようです。クラス図で表現すると次のようになります。

<img width="726" alt="スクリーンショット 2020-08-06 9.44.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500854/df0668ab-5cb7-640d-e748-c967519a01c1.png">

委譲を利用する方法は、Targetがインタフェースではなくクラスである場合に便利です。

```php
// Targetがクラスである場合
abstract class PrintAbstract
{
    abstract public function printWeak(): void;
    abstract public function printStrong(): void;
}
```

さきほどはAdpterがAdapteeを継承することでAdapterパターンを実現できましたが、複数のクラスを同時に継承することはできないため、Targetがクラスである場合にはそれが使えません。代わりに、委譲を利用して次のように実現します。

```php
class PrintBanner extends PrintAbstract
{
    private $banner; // Bannerをプロパティとして持つ

    public function __construct(string $string)
    {
        $this->banner = new Banner($string);
    }

    public function printWeak(): void
    {
        $this->banner->showWithParen(); // 委譲を利用してBannerの関数を呼び出す
    }

    public function printStrong(): void
    {
        $this->banner->showWithAster();
    }
}
```

### Adapterパターンのメリット

Adapterパターンは、既存のクラスを修正することなく、インタフェースを変更するための手法です。

既存のクラスを使い回すことのメリットには、次のようなものがあるようです。

* ゼロから実装するよりも早く完成する
* 既存のクラスがテスト済みである場合は、確実に動作するものを使いまわせる
  * バグが発生した場合は、Adapter側に原因があることが明らかなので修正も容易
  * 既存のクラスは修正しないため、テストを書き直す必要がない
* 委譲を利用すればTargetがクラスである場合も他のクラスの実装を利用できる

参考までに、Adapterパターン実装例（PHP）を載せておきます。

https://github.com/yuta-o-note/gof-design-pattern

```php
<?php

/**
 * 継承を利用して実現するパターン
 */

interface PrintInterface
{
    public function printWeak(): void;
    public function printStrong(): void;
}

class Banner
{
    private $string;

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

    public function showWithParen(): void
    {
        echo '(' . $this->string . ')' . "\n";
    }

    public function showWithAster(): void
    {
        echo '*' . $this->string . '*' . "\n";
    }
}

class PrintBanner extends Banner implements PrintInterface
{
    public function printWeak(): void
    {
        $this->showWithParen();
    }

    public function printStrong(): void
    {
        $this->showWithAster();
    }
}

function main(PrintInterface $print)
{
    $print->printWeak();
    $print->printStrong();
}

$print = new PrintBanner('Hello');
main($print);
```

```php
<?php

/**
 * 委譲を利用して実現するパターン
 */

abstract class PrintAbstract
{
    abstract public function printWeak(): void;
    abstract public function printStrong(): void;
}

class Banner
{
    private $string;

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

    public function showWithParen(): void
    {
        echo '(' . $this->string . ')' . "\n";
    }

    public function showWithAster(): void
    {
        echo '*' . $this->string . '*' . "\n";
    }
}

class PrintBanner extends PrintAbstract
{
    private $banner;

    public function __construct(string $string)
    {
        $this->banner = new Banner($string);
    }

    public function printWeak(): void
    {
        $this->banner->showWithParen();
    }

    public function printStrong(): void
    {
        $this->banner->showWithAster();
    }
}

function main()
{
    $print = new PrintBanner('Hello');
    $print->printWeak();
    $print->printStrong();
}

main();
```