【PHPデザインパターン】03_Adapter～APIを変更する

# 引用記事

この記事を書くきっかけになったブログです。

記事内の解説やソースコードは、こちらのブログと著者の公開リポジトリを参考にしています。

> [Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Adapter～APIを変更する](http://d.hatena.ne.jp/shimooka/20141212/1418364494)

# 概要

- インターフェイスに互換性のないクラス同士を組み合わせることを目的とする。
- 既存のクラスに対して修正を加えることなく、他のインターフェイスへ変更する。
- ``Targetクラス``を使用したいが、具体的な処理は``Adapteeクラス``に任せたい。それを適合させるために``Adapterクラス``でインターフェイスを実装する。
- クライアント側からはインターフェイス（Adapterクラス）しか見えなくなり、その向こうにある処理の実装（TargetクラスとAdapteeクラス）を意識する必要がなくなる。
- パターンを再現する方法は、``継承``と``委譲``の2種類がある。
- 既存のクラスを変更するのではなく、一枚皮をかぶせるようなクラスを作る。そのことから``Wrapperパターン``とも呼ばれる。

# 構成

## 処理の流れ

### 共通点
- クライアント側から``DisplaySourceFileインターフェイス``を利用して``MyShowFileクラス``のメソッドを使用したい。
- そのアダプター（つなぎ役）として``DisplaySourceFileAdapterクラス``を作成する。
- DisplaySourceFileインターフェイスを定義し、DisplaySourceFileAdapterクラスに実装させる。
- DisplaySourceFileインターフェイスでは``displayメソッド``を定義している。（実装先のクラスではメソッドの実装が必須となる。）
- displayメソッドの具体的な実装は、MyShowFileクラスのメソッドを利用して行う。

### 継承
- DisplaySourceFileAdapterクラスが、MyShowFileクラスを継承する。
- コンストラクタで親クラス（MyShowFileクラス）のコンストラクタを呼び出し、親クラスにプロパティとして``$array``をセットする。
- 実装したdisplayメソッドから親クラス（MyShowFileクラス）のメソッドを呼び出す。セットした$arrayがその時に使用される。

### 委譲
- DisplaySourceFileAdapterクラス内で、MyShowFileクラスのインスタンスを作成する。
- コンストラクタでMyShowFileクラスのインスタンスを作成し、DisplaySourceFileAdapterクラスのプロパティとして``$array_obj``をセットする。同時に親クラスのプロパティとして``$array``もセットされる。
- 実装したdisplayメソッドから、MyShowFileクラスのメソッドを呼び出す。セットした$arrayがその時に使用される。

## ファイル構成

```
MyAdapter
   ├── Ext
   │   ├── DisplaySourceFile.php
   │   └── DisplaySourceFileAdapter.php
   ├── Impl
   │   ├── DisplaySourceFile.php
   │   └── DisplaySourceFileAdapter.php
   ├── MyShowFile.php
   ├── my_client_ext.php
   └── my_client_lmpl.php
```

## ソースコード

### 共通ファイル

```MyShowFile.php
<?php
namespace DoYouPhp\PhpDesignPattern\Adapter\MyAdapter;

class MyShowFile
{
    // プロパティを宣言する
    private $array;

    // コンストラクタ(オブジェクト生成時にコールされるメソッド)
    // 配列でなければ例外を返す
    public function __construct($array)
    {
        if (!is_array($array)) {
            throw new \Exception('配列を渡してください');
        }

        $this->array = $array;
    }

    // 配列を改行で返す
    public function BreakArray()
    {
        foreach ($this->array as $value) {
            echo $value."<br>"."\n";
        }
    }

    // 配列をタブ区切りで返す
    public function TabArray()
    {
        foreach ($this->array as $value) {
            echo $value."\t";
        }
    }
}
```

### 継承

```my_client_ext.php
<?php
namespace DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Ext;

require dirname(dirname(__DIR__)).'/vendor/autoload.php';

use DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Ext\DisplaySourceFileAdapter;

$array = array('apple', 'orange', 'peach', 'banana');

try {
    // DisplaySourceFileAdapterクラスをインスタンス化する
    // コンストラクタで、親クラスであるMyShowFileクラスのコンストラクタを呼び出している
    // 配列がMyShowFileクラスのプロパティである$arrayにセットされる
    // 引数が配列でない場合は、例外を返す
    // 引数を指定しない場合は、Fatal errorになる
    $show_array = new DisplaySourceFileAdapter($array);

    // 呼び出されているのはDisplaySourceFileインターフェイスのメソッド
    // メソッド内で親クラスであるMyShowFileクラスのメソッドが呼び出され、$arrayが使用される
    $show_array->display();

} catch (\Exception $e) {
    echo $e->getMessage()."<br>"."\n";
}
```

```Ext/DisplaySourceFile.php
<?php
namespace DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Ext;

// 実装先には、インターフェイスに含まれるメソッドを全て定義させる必要がある
// インターフェイス内でメソッドが実装されることはない
// メソッドは全てpublicである必要がある
interface DisplaySourceFile
{
    public function display();
}
```

```Ext/DisplaySourceFileAdapter.php
<?php
namespace DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Ext;

use DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Ext\DisplaySourceFile;
use DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\MyShowFile;

// MyShowFileを継承し、DisplaySourceFileインターフェイスを実装する
class DisplaySourceFileAdapter extends MyShowFile implements DisplaySourceFile
{
    // コンストラクタ(オブジェクト生成時にコールされるメソッド)
    // 親クラスのコンストラクタは、サブクラスで自動的に呼び出されることはない
    // そのため、parentキーワードでMyShowFileクラスのコンストラクタを呼び出している
    public function __construct($array)
    {
        parent::__construct($array);
    }

    // インターフェイスに定義されたメソッド
    // 親クラスであるMyShowFileクラスのメソッドを呼び出す
    public function display()
    {
        parent::TabArray();
    }
}
```

### 委譲

```my_client_lmpl.php
<?php
namespace DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Impl;

require dirname(dirname(__DIR__)).'/vendor/autoload.php';

use DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Impl\DisplaySourceFileAdapter;

$array = array('apple', 'orange', 'peach', 'banana');

try {
    // DisplaySourceFileAdapterクラスをインスタンス化する。
    // コンストラクタでMyShowFileクラスのインスタンスが作成される
    // 引数が配列でない場合は、例外を返す
    // 引数を指定しない場合は、Fatal errorになる
    $show_array = new DisplaySourceFileAdapter($array);

    // 呼び出されているのはDisplaySourceFileインターフェイスのメソッド
    // メソッド内でMyShowFileクラスのメソッドが呼び出されている
    $show_array->display();

} catch (\Exception $e) {
    echo $e->getMessage()."<br>"."\n";
}
```

```Impl/DisplaySourceFile.php
<?php
namespace DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Impl;

// 実装先には、インターフェイスに含まれるメソッドを全て定義させる必要がある
// インターフェイス内でメソッドが実装されることはない
// メソッドは全てpublicである必要がある
interface DisplaySourceFile
{
    public function display();
}
```

```Impl/DisplaySourceFileAdapter.php
<?php
namespace DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Impl;

use DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\Impl\DisplaySourceFile;
use DoYouPhp\PhpDesignPattern\Adapter\MyAdapter\MyShowFile;

// implements演算子で、DisplaySourceFileインターフェイスを実装する
// インターフェイスに定義されたメソッドは必ず実装する必要がある
class DisplaySourceFileAdapter implements DisplaySourceFile
{
    // プロパティを宣言する
    // MyShowFileクラスのインスタンスがセットされる
    private $array_obj;

    // コンストラクタ(オブジェクト生成時にコールされるメソッド)
    // インポートしたMyShowFileクラスのインスタンスを作成する
    public function __construct($array)
    {
        $this->array_obj = new MyShowFile($array);
    }

    // インターフェイスに定義されたメソッド
    // 作成したMyShowFileクラスのインスタンスからメソッドを呼び出す
    public function display()
    {
        $this->array_obj->TabArray();
    }
}
```