依存の再束縛
11日目の記事アプリケーションコンテキストと9日目の@zukimochiさんの記事BEAR.SundayのRouterCollectionに関連した内容です。
Ray.Di 2.0の新機能、依存の再束縛です。
インターフェイスとクラスの接続(束縛)を後から変更することができます。
ユースケース
Before
例えばBEAR.SundayのRouterCollectionの記事では元々
$this->bind(RouterInterface::class)->(WebRouter::class);
と束縛しているのを
$this->bind(RouterInterface::class)->(RouterCollectionProvider::class);
と束縛を上書きして、RouterCollectionProvider
ではRouterCollection
という以下のように2つのルーターを受け取ってグルグル回していました。
/**
* @Inject
* @Named("auraRouter=aura_router,webRouter=web_router")
*/
public function __construct(AdapterInterface $auraRouter, AdapterInterface $webRouter)
{
WebRouter
からWebRouter
とAuraRouter
を持つRouterCollection
へと変更です。
After
これを「依存の再束縛」を使って現在束縛されているルーターとAuraRouter
を持つRouterCollection
へと変更してみます。
まず現在のRouterInterface
にされている束縛にoriginal
と名前をつけます。
$this->rename(RouterInterface::class, 'original');
つぎに新しいルーターを束縛します。
$this->bind(RouterInterface::class)->(RouterCollection::class);
RouterCollection
ではAdapterInterface
のoriginal
という名前で以前の束縛を利用することができます。オリジナルの束縛が変わってもコードに変更はありません!
/**
* @Inject
* @Named("original")
*/
public function __construct(AdapterInterface $router)
{
アプリケーションのデコレーション
cli
コンテキストではこの依存の再束縛を使っています。
class CliModule extends AbstractModule
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->rename(RouterInterface::class, 'original');
$this->bind(RouterInterface::class)->to(CliRouter::class);
$this->bind(TransferInterface::class)->to(CliResponder::class);
}
}
コンソールから入力された値をRouterInterface
に束縛されてあったルーターに渡します。
class CliRouter implements RouterInterface
{
private $router;
/**
* @Inject
* @Named("original")
*/
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function match(array $globals = [])
{
list(, $method, $uri) = $globals['argv'];
$globals = [
// ...コンソールから受け取った値で$globalsを作成
];
return $this->router->match($globals);
}
}
まとめ
ルーターインターフェイスに束縛されてルーターをそれが何のルーターであるかを知らずに利用して、コンソールから受け取った引数を渡してインターフェイスにあるメソッドを使って元のルーターを利用しています。元のwebアプリケーションに変更することなしに、コンソールアプリケーションにデコレーションすることができました!
「抽象」は実装の詳細に依存してはならない。実装の詳細が「抽象」に依存すべきである。
この依存性逆転の原則が可能にした機能です。
__これが何のルーターであるかを知らずに__は「実装の詳細に依存してはならない」に対応して、__インターフェイスにあるメソッドを使って元のルーターを利用__が「実装の詳細が「抽象」に依存すべき」に対応します。
明日は@mackstarさんの「BEARとBDD」です。楽しみにしてます!