本記事の範囲
本記事では、サービスロケータ を記事の範囲とします。 DI 、DI コンテナについては別記事で書いています。
サービスロケータとは
「サービスロケータ」とは、サービス(オブジェクト)の取得を抽象化するデザインパターンです。
サービスロケータの例
<?php
require_once './vendor/autoload.php';
use Pimple\Container;
class Client
{
private $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function log($messgae)
{
$this->container['service_interface']->log();
}
}
interface ServiceInterface
{
public function log();
}
class Service implements ServiceInterface
{
public function log($message)
{
echo $message . "\n";
}
}
// Client呼び出し
$container = new Container();
$container['service_interface'] = function ($container) {
$service = new Service();
return $service;
};
$container['client'] = function ($container) {
$client = new Client($container);
return $client;
};
$client = $container['client'];
$client->log('ssssss');
ここでは、$container
がサービスロケータとなります。
依存するものが DIとサービスロケータで異なります。
DIの場合
- ServiceInterface を実装しているオブジェクト
サービスロケータの場合
- DIコンテナ (Pimple)
$this->container['service_interface']
これによって、Client クラスが DIコンテナ (Pimple)の依存が強くなり、Client を利用するときは DIコンテナも必要とされるようになってしまいました。
DIコンテナについてまず知っておくべきなのは、DIコンテナを使ってさえいれば「依存性の注入」ができるわけではないってことだ。 DIコンテナは、依存性の注入を実現するための便利な道具として使える。 でも、使い方をミスって、サービスロケーションというアンチパターンを作ってしまっていることも多い。 DIコンテナをサービスロケーターとしてクラスに組み込んでしまうと、 依存関係を別の場所に移そうとしていたはずなのに、よりきつい依存関係を作り込むことになる。 おまけにそのコードはわかりにくくなってしまうし、テストもしづらくなる。
結局のところ、サービスロケータはアンチパターンのようです。