一言で言うと?
異なるインターフェース、クラスの利用問題等を解決してくれる。(AというクラスとBというクラスがあり、それらの両方の機能を利用したいが、便宜上、互いに呼び出せない時にアダプターとなるCクラスを作り、AもBも利用するというイメージ)
どんなシチュエーションで使うの?
- 前述にあるように、2つ以上のクラスの機能を効率よく利用したい時など。
パターン
Adapterパターンには「継承」と「委譲」の二つのパターンがある。
本記事では「継承」をメインに解説。
実装
実装の概要(継承をメインに)
- ロボットクラスと掃除機クラスが存在する(VacuumCleanerクラスとRobotクラス)。
- ロボットクラスと掃除機クラスはそれぞれ「AI」と「掃除」インターフェースを実装している。(ArtificialIntelligenceInterfaceとCleanUpInterface)。
- AIインターフェースではAI機能の作動を司るメソッドを定義。
- 掃除インターフェースでは掃除機能の作動を司るメソッドを定義。
- AIインターフェースを実装したロボットクラス、掃除インターフェースを実装した掃除機クラスでそれぞれクライアント側では呼び出されないprivateなメソッドを実装し、具体的な処理を記述する。
- 上記の2クラス(インターフェース)の機能を掛け合わせ、AI機能搭載のお掃除ロボットを作りたい(上記の2クラスの両方の実装内容をひとまとめにして利用したい。)。
- 掃除機クラスを継承し、さらにAIインターフェースを実装したルンバクラスを作ることにする。
- 上記の実装であれば、掃除機クラスの機能がそのまま使え、さらに、AIインターフェースで定義されているメソッドによって実行される具体的な処理に関するprivateなメソッドを再び実装すれば、全ての機能が利用できる。
サンプルコード
ロボットクラス
Robot.php
<?php
namespace App;
use App\Interfaces\ArtificialIntelligenceInterface;
require 'vendor/autoload.php';
class Robot implements ArtificialIntelligenceInterface
{
/**
* 自動的に動く
*
* Undocumented function
*
* @return void
*/
private function automaticallyMove(): void
{
echo '2時間自動的に動きます。';
}
/**
* AI機能の作動
*
* Undocumented function
*
* @return void
*/
public function operateArtificialIntelligence(): void
{
$this->automaticallyMove();
}
}
掃除機クラス
VacuumCleaner.php
<?php
namespace App;
use App\Interfaces\CleanUpInterface;
require 'vendor/autoload.php';
class VacuumCleaner implements CleanUpInterface
{
/**
* 掃除する
*
* Undocumented function
*
* @return void
*/
private function clean(): void
{
echo 'ゴミを吸い取ります。' . "\n";
}
/**
* 作動する
*
* Undocumented function
*
* @return void
*/
public function operateCleanUp(): void
{
$this->clean();
}
}
AIインターフェース
ArtificialIntelligenceInterface.php
<?php
namespace App\Interfaces;
interface ArtificialIntelligenceInterface
{
// AI機能の作動
public function operateArtificialIntelligence();
}
掃除インターフェース
CleanUpInterface.php
<?php
namespace App\Interfaces;
interface CleanUpInterface
{
// 掃除機能の作動
public function operateCleanUp();
}
ルンバクラス
Roomba.php
<?php
namespace App;
use App\VacuumCleaner;
use App\Interfaces\ArtificialIntelligenceInterface;
require 'vendor/autoload.php';
class Roomba extends VacuumCleaner implements ArtificialIntelligenceInterface
{
/**
* 自動的に動く
*
* Undocumented function
*
* @return void
*/
private function automaticallyMove(): void
{
echo '2時間自動的に動きます。' . "\n";
}
/**
* AI機能の作動
*
* Undocumented function
*
* @return void
*/
public function operateArtificialIntelligence(): void
{
$this->automaticallyMove();
}
}
実行クラス
Adapter.php
<?php
namespace App;
use App\Roomba;
require 'vendor/autoload.php';
$ai_cleaning_robot = new Roomba();
$ai_cleaning_robot->operateArtificialIntelligence();
$ai_cleaning_robot->operateCleanUp();
実行結果
2時間自動的に動きます。
ゴミを吸い取ります。
「継承」ではなく「委譲」のパターンを採用する場合
ルンバクラス
Roomba2.php
<?php
namespace App;
use App\VacuumCleaner;
use App\Interfaces\ArtificialIntelligenceInterface;
require 'vendor/autoload.php';
class Roomba2 implements ArtificialIntelligenceInterface
{
// VacuumCleanerクラスのインスタンス格納用
private $vacuum_cleaner;
public function __construct()
{
$this->vacuum_cleaner = new VacuumCleaner();
}
/**
* 自動的に動く
*
* Undocumented function
*
* @return void
*/
private function automaticallyMove(): void
{
echo '2時間自動的に動きます。' . "\n";
}
/**
* AI機能の作動
*
* Undocumented function
*
* @return void
*/
public function operateArtificialIntelligence(): void
{
$this->automaticallyMove();
}
/**
* 掃除機クラスのメソッド呼び出し
*
* Undocumented function
*
* @return void
*/
public function callOperateCleanUp(): void
{
$this->vacuum_cleaner->operateCleanUp();
}
}
実行クラス
Adapter2.php
<?php
namespace App;
use App\Roomba2;
require 'vendor/autoload.php';
$ai_cleaning_robot = new Roomba2();
$ai_cleaning_robot->operateArtificialIntelligence();
$ai_cleaning_robot->callOperateCleanUp();
「委譲」の場合は継承を使用せずインスタンスをコンストラクタでプロパティに渡している。
少し、ルンバクラス内のクライアント呼び出し用のメソッドが増え、実行クラスのコードも修正が必要になった。
気をつけたい地味なポイント
- Interfaceの使用方法 (クライアント側から隠すメソッドや、インターフェース定義による呼び出しの誘導等)
参考
PHPによるデザインパターン入門 - Adapter〜APIを変更する
http://shimooka.hateblo.jp/entry/20141212/1418364494