用trait實現簡單的依賴注入

這裏先假設一個場景:函數

有一個工廠(Factory),須要工人(Worker) 使用某個機器(Machine)來生產某種產品測試

即有這樣的依賴關係: Factory --> Worker --> Machinethis

不使用注入

代碼大概是這樣子:code

class Machine{
    function run(){
        echo '機器開動';
    }
}
class Worker {
    function work(){
        echo "工人開動機器 -> ";
        $machine = new Machine();
        $machine -> run();
    }
}
class Factory{
    function produce(){
        echo "工廠叫工人開工 -> ";
        $worker = new Worker();
        $worker -> work();
    }
}
$factory = new Factory();
$factory -> produce();

能夠看出來,這裏所依賴的對象都由類本身在內部實例化,是一種強耦合,不利於測試和維護。好比我如今要改爲另外一種工人來生產,那就要改工廠內部,這是不合理的。對象

手工注入

所謂的注入,就是將類所依賴的對象的實例化操做放在類外面,同時使用Interface來做出約束:接口

Interface Machine{
    public function run();
}
Interface Worker{
    public function work();
}
class Machine1 implements Machine{
    function run(){
        echo '機器 1 開動';
    }
}
class Machine2 implements Machine{
    function run(){
        echo '機器 2 開動';
    }
}

class Worker1 implements Worker{
    private $machine;
    public function __construct(Machine $machine){
        $this -> machine = $machine;
    }
    function work(){
        echo "工人 1 開動機器 -> ";
        $this -> machine -> run();
    }
}
class Worker2 implements Worker{
    private $machine;
    public function __construct(Machine $machine){
        $this -> machine = $machine;
    }
    function work(){
        echo "工人 2 開動機器 -> ";
        $this -> machine -> run();
    }
}

class Factory{
    private $worker;
    public function __construct(Worker $worker){
        $this -> worker = $worker;
    }
    function produce(){
        echo "工廠叫工人開工 -> ";
        $this -> worker -> work();
    }
}

$machine = new Machine1();
$worker = new Worker2($machine);
$factory = new Factory($worker);

$factory -> produce();

能夠看出來,這樣的好處是解耦。好比:能夠由工人1開動機器2來生產,也能夠由工人2來開動機器1來生產,之後也能夠隨時用新的工人(只要他會work)、Worker也能夠隨時換其它的機器(只要它會run)來生產。這種轉換都不須要修改工廠或工人的代碼。get

那麼問題來了,如今只是把實例化從裏面移到了外面,但若是依賴的東西多了,也是很麻煩的,這就須要一個自動注入的機制,也就是平時常常聽到的注入容器,常見的容器都是用到反射來實現,並且要在構造函數中聲明注入的類型,相對仍是比較麻煩。產品

在本篇,我嘗試用另外一種方式實現。it

trait自動注入

trait能夠簡單理解爲能夠複用的方法,下面來看看怎麼用trait來實現自動注入。io

思路就是用trait來實現魔術方法__get,經過該方法來自動生成依賴的對象,先看完整代碼

trait DI{
    private $map = ['Worker' => 'Worker1','Machine'=>'Machine2'];

    function __get($k){
        if(preg_match('/^[A-Z]/', $k)) {
            $obj =  new $this -> map[$k];
            if($obj instanceof $k){
                return $obj;
            }else{
                exit("不符約定");
            }
        }
    }
}

Interface Machine{
    public function run();
}
Interface Worker{
    public function work();
}
class Machine1 implements Machine{
    function run(){
        echo '機器 1 開動';
    }
}
class Machine2 implements Machine{
    function run(){
        echo '機器 2 開動';
    }
}
class Worker1 implements Worker{
    use DI;
    function work(){
        echo "工人 1 開動機器 -> ";
        $this -> Machine -> run();
    }
}
class Worker2 implements Worker{
    use DI;
    function work(){
        echo "工人 2 開動機器 -> ";
        $this -> Machine -> run();
    }
}
class Factory{
    use DI;
    function produce(){
        echo "工廠叫工人開工 -> ";
        $this -> Worker -> work();
    }
}

$factory = new Factory();
$factory -> produce();
  • trait中的map用來演示實現類和接口的綁定關係,以便進行類型約束,實際應用時能夠用配置或其它方式實現.
  • 類中要使用依賴注入時,需聲明use DI, 同時,注入的對象爲首字母大寫(你也能夠用其它規則,相應的修改trait中的判斷)

固然了,這只是一個很粗糙的演示,只實現了基本的自動注入,它還有不少問題,好比原來的類若是也有__get方法時,就會產生覆蓋。

有興趣的能夠嘗試完善一下看能不能在項目中實際使用。

相關文章
相關標籤/搜索