実はわかっていない人が多いのではということで今回はDependency Inject(=DI)について説明を行っていきます。

Dependencyは”依存”という意味を持ちInjectは”何かを入れる”という意味があります。個人的には、”依存関係のあるものを外側から入れる”という理解をしています。

言葉の説明では大半の人がわからないと思うので、コードを確認して”依存関係のあるものを外側から入れる”を見てみましょう。

Dependencyとは

まずDependency(依存関係)を2つのクラスを使って説明します。

以下のコードのようにSlackクラスとMyClassクラスを作成します。Slackクラスはsendメソッドを持つだけのシンプルなクラスです。しかしMyClassクラスはSlackクラスのsendメソッドをrunメソッドの中で使用するためSlackクラスが必須となります。これがMyClassクラスはSlackクラスに依存していることを表し、2つのクラスが依存関係を持つことになります。

denpendencyの対義語はindepnedencyで独立を意味します。

class Slack{

    public function send(){

        var_dump('something happens');

    }

}

class MyClass{
    
    public $slack;

    public function __construct(){

        $this->slack = new Slack();

    }

    public function run(){

        $this->slack->send();

    }
 
}

$myclass = new MyClass();

$myclass->run();

Dependency Injectionとは

クラス間の依存関係が理解できたので、Dependency Injectionの話に進みます。上記のコードを見るとSlackクラスはMyClassの中のコンストラクターで作成されており内側で処理されていることがわかります。

ではクラスを外側から入れてみましょう。


class Slack{
    public function send(){
        var_dump('something happens');
    }
}


class MyClass{

    public $slack;

    public function __construct(Slack $slack){

        $this->slack = $slack;

    }

    public function run(){

        $this->slack->send();

    }
    
}

$myclass = new MyClass(new Slack());

$myclass->run();

これがDependency Injectionです。これで”依存関係のあるものを外側から入れる”という意味が理解できたかと思います。

またコンストラクター部分でDependency Injectionが行われているので、Constructor Injection(コンストラクターインジェクション)と呼ばれます。

Setter Injectionとは

先程は、Constructor Injectionで依存関係のあるクラスを外側から入れましたが、Setter Injectionというものもあります。コードを見ればわかりますが、Constructorではなくメソッドの部分でDependency Injectionを行います。


class Slack{
    public function send(){
        var_dump('something happens');
    }
}

class MyClass{

    public $slack;

    public function set(Slack $slack){
        
        $this->slack = $slack;

    }

    public function run(){

        $this->slack->send();

    }
    
}

$myclass = new MyClass();

$myclass->set(new Slack());

$myclass->run();

これも同様に依存関係のあるものを外側から入れていることがわかります。

Interfaceを利用したDI

上記までの例ではSlackクラスを使っていましたが、SlackはやめてMailクラスを使うことになった場合は、Interfaceを利用していれば簡単にSlackクラスとMailクラスを取り替えることができます。

Messageインターフェイスを作成して、sendメソッドをもたせます。


interface Message{
    public function send();
}

Slack、MailクラスをMessageを継承して作成します。そのため、それぞれsendメソッドを持たせる必要があります。


class Slack implements Message{
    public function send(){
        var_dump('something happens by slack');
    }
}

class Mail implements Message{
    public function send(){
        var_dump('something happens by mail');
    }
}
ここでは単純化してsendメソッドでvar_dumpしているだけですが、実際にはslackとmailではsendメソッドの中の処理は全く異なります。

これまでDependency Injectionではクラスを使っていましたが、Messageインターフェイスに変更します。


class MyClass{

    public $message;

    public function __construct(Message $message){

        $this->message = $message;

    }

    public function run(){

        $this->message->send();

    }
    
}

この結果、外側から入れるクラスを変更することでSlackクラスとMailクラスを簡単に取替えることができます。


$myclass = new MyClass(new Mail());

$myclass->run();

$myclass = new MyClass(new Slack());

$myclass->run();

結果は下記のように表示されます。


string(25) "something happens by mail"
string(26) "something happens by slack"
このあたりの説明を他の文書で見るとAbstract(抽象)とかConcrete(具象)とか難しい言葉が出てきますが、Messageインターフェイスは抽象クラスでSlack, Mailは具象クラスに対応します。