BEAR.Sunday

依存の再束縛

More than 1 year has passed since last update.

依存の再束縛

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からWebRouterAuraRouterを持つRouterCollectionへと変更です。

After

これを「依存の再束縛」を使って現在束縛されているルーターAuraRouterを持つRouterCollectionへと変更してみます。

まず現在のRouterInterfaceにされている束縛にoriginalと名前をつけます。

$this->rename(RouterInterface::class, 'original');

つぎに新しいルーターを束縛します。

$this->bind(RouterInterface::class)->(RouterCollection::class);

RouterCollectionではAdapterInterfaceoriginalという名前で以前の束縛を利用することができます。オリジナルの束縛が変わってもコードに変更はありません!

    /**
     * @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」です。楽しみにしてます!