這裏先假設一個場景:函數
有一個工廠(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來實現自動注入。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();
固然了,這只是一個很粗糙的演示,只實現了基本的自動注入,它還有不少問題,好比原來的類若是也有__get方法時,就會產生覆蓋。
有興趣的能夠嘗試完善一下看能不能在項目中實際使用。