Adapter
TweetInterfaceと実装の中間装置的なデザインパターン。
既存のクラスに変更を加えることなく、APIを変更することができるのが利点。
例
FWとかに依存せずにusers
テーブルからユーザ情報の一覧をとってくる
UML
重要なところ
どのような処理を行うかのInterfaceを定義する。
あとは、Userユーザデータを実際に取得するロジックを呼び出すような中間装置的な役割を持つAdapter
を実装する。
以下は、CodeIgniterを利用した例
また、UserFetcharはCI_Db_query_builderクラスなどFW固有のクラスには一切依存していない。
これにより、クライアントコードをUserFetcherインターフェースに依存すれば、FWに依存せずユーザ情報が取得できる。
例えば、Laravelに依存する場合は以下のようにAdapterとFW固有のクラスが変わるだけ。
また、これらは委譲を行うパターンだが、継承を利用するパターンもある。
PHPで書いてみる
UserFetcher(Interface)1
2
3interface UserFetcher {
public function get() : array;
}
User(Entity)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class User {
private $name;
public function __construct(array $config = [])
{
if (!empty($config['name'])) {
$this->name = $config['name']
}
}
public function getName()
{
return $this->name;
}
}
Adapter(CodeIgniterのパターン)1
2
3
4
5
6
7
8
9
10
11
12
13
14class Adapter implement UserFetcher
{
private $db;
public function __construct(CI_Db_query_builder $db)
{
$this->db = $db;
}
public function get(int $id) : array
{
reutrn $this->db->get('users')->result(User::class);
}
}
Client Code1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class NameRenderer {
private $fetcher;
public function __construct(UserFetcher $fetcher) // <- 抽象に依存
{
$this->fetcher = $fetcher;
}
public function render()
{
$users = $this->fetcher->get();
foreach ($users as $user) {
echo $user->getName() . 'さん、こんにちは';
}
}
}
$fetcher = new Adapter(get_instance()->db);
// NameRendererは、UserFetcherのインターフェースが利用できれば良い
$renderer = new NameRenderer($fetcher);
$renderer->render();
この様に、NameRendererがほしいUserFetcher Interfaceの中間装置
的な役割を行うAdapter
を変更すれば、クライアントコードが実装可能になる。
また、これは委譲を行うパターンだが、継承を利用するパターンもある。
1 | class TarouFamilyFactory { |
Adapter(継承を利用するパターン)1
2
3
4
5
6
7
8
9
10
11
12
13class Adapter extends TarouFamilyFactory implement UserFetcher
{
public function get() : array
{
// 親クラスのメソッドを、Interfaceに合わせた形で提供する
return [
$this->createFather(), // 親クラスのメソッドを利用する
$this->createMother(),
$this->createBrother(),
$this->createTaro(),
];
}
}
まとめ
Interface
と既存クラス
の橋渡しを行う- 委譲でも継承でも実装しても良い