當A類須要依賴於B類,也就是說須要在A類中實例化B類的對象來使用時候,若是B類中的功能發生改變,也會致使A類中使用B類的地方也要跟着修改,致使A類與B類高耦合。這個時候解決方式是,A類應該去依賴B類的接口,把具體的類的實例化交給外部。
就拿咱們業務中經常使用的通知模塊來講。php
<?php /** * 定義了一個消息類 * Class Message */ class Message{ public function seed() { return 'seed email'; } } /* * 訂單產生的時候 須要發送消息 */ class Order{ protected $messager = ''; function __construct() { $this->messager = new Message(); } public function seed_msg() { return $this->messager->seed(); } } $Order = new Order(); $Order->seed_msg();
上面的代碼是咱們傳統的寫法。首先由個消息發送的類。而後在咱們須要發送消息的地方,調用發送消息的接口。有一天你須要添加一個發送短信的接口以知足不一樣的需求。那麼你會發現你要再Message
類裏面作修改。一樣也要再Order
類裏面作修改。這樣就顯得很麻煩。這個時候就有了依賴注入的思路。下面把代碼作一個調整html
<?php /** * 爲了約束咱們先定義一個消息接口 * Interface Message */ interface Message{ public function seed(); } /** * 有一個發送郵件的類 * Class SeedEmail */ class SeedEmail implements Message { public function seed() { return 'seed email'; // TODO: Implement seed() method. } } /** *新增一個發送短信的類 * Class SeedSMS */ class SeedSMS implements Message { public function seed() { return 'seed sms'; // TODO: Implement seed() method. } } /* * 訂單產生的時候 須要發送消息 */ class Order{ protected $messager = ''; function __construct(Message $message) { $this->messager = $message; } public function seed_msg() { return $this->messager->seed(); } } //咱們須要發送郵件的時候 $message = new SeedEmail(); //將郵件發送對象做爲參數傳遞給Order $Order = new Order($message); $Order->seed_msg(); //咱們須要發送短信的時候 $message = new SeedSMS(); $Order = new Order($message); $Order->seed_msg();
這樣咱們就實現了依賴注入的思路,是否是很方便擴展了。閉包
我理解的服務容器就是一個自動產生類的工廠。
<?php /** * 爲了約束咱們先定義一個消息接口 * Interface Message */ interface Message{ public function seed(); } /** * 有一個發送郵件的類 * Class SeedEmail */ class SeedEmail implements Message { public function seed() { return 'seed email'; // TODO: Implement seed() method. } } /** *新增一個發送短信的類 * Class SeedSMS */ class SeedSMS implements Message { public function seed() { return 'seed sms'; // TODO: Implement seed() method. } } /** * 這是一個簡單的服務容器 * Class Container */ class Container { protected $binds; protected $instances; public function bind($abstract, $concrete) { if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } public function make($abstract, $parameters = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); } } //建立一個消息工廠 $message = new Container(); //將發送短信註冊綁定到工廠裏面 $message->bind('SMS',function (){ return new SeedSMS(); }); //將發送郵件註冊綁定到工廠 $message->bind('EMAIL',function (){ return new SeedEmail(); }); //須要發送短信的時候 $SMS = $message->make('SMS'); $SMS->seed();
container
是一個簡單的服務容器裏面有bind
,make
兩個方法bind
是向容器中綁定服務對象。make
則是從容器中取出對象。函數
在bind
方法中須要傳入一個 concrete
咱們能夠傳入一個實例對象或者是一個閉包函數。
能夠看到我這全使用的是閉包函數,其實也能夠這樣寫this
$sms = new SeedSMS(); $message->bind('SMS',$sms);
後面這種寫法與閉包相比的區別就是咱們須要先實例化對象才能往容易中綁定服務。而閉包則是咱們使用這個服務的時候纔去實例化對象。能夠看出閉包是有不少的優點的。.net
make
方法就從容器中出去方法。裏面首先判斷了instances
變量中是否有當前以及存在的服務對象,若是有直接返回。若是沒有那麼會經過 call_user_func_array
返回一個對象。call_user_func_array
的使用能夠查看
PHP 中 call_user_func 的使用 code
原文地址htm