国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

搞懂依賴注入, 用 PHP 手寫(xiě)簡(jiǎn)易 IOC 容器

antz / 1546人閱讀

摘要:依賴注入控制反轉(zhuǎn)的一種具體實(shí)現(xiàn)方法。接下來(lái),我們使用依賴注入實(shí)現(xiàn)控制反轉(zhuǎn),使依賴關(guān)系倒置依賴被動(dòng)傳入。從單元測(cè)試的角度看,依賴注入更方便和操作,方便了測(cè)試人員寫(xiě)出質(zhì)量更高的測(cè)試代碼。

前言

好的設(shè)計(jì)會(huì)提高程序的可復(fù)用性和可維護(hù)性,也間接的提高了開(kāi)發(fā)人員的生產(chǎn)力。今天,我們就來(lái)說(shuō)一下在很多框架中都使用的依賴注入。

一些概念

要搞清楚什么是依賴注入如何依賴注入,首先我們要明確一些概念。

DIP (Dependence Inversion Principle) 依賴倒置原則:

程序要依賴于抽象接口,不要依賴于具體實(shí)現(xiàn)。

IOC (Inversion of Control) 控制反轉(zhuǎn):

遵循依賴倒置原則的一種代碼設(shè)計(jì)方案,依賴的創(chuàng)建 (控制) 由主動(dòng)變?yōu)楸粍?dòng) (反轉(zhuǎn))。

DI (Dependency Injection) 依賴注入:

控制反轉(zhuǎn)的一種具體實(shí)現(xiàn)方法。通過(guò)參數(shù)的方式從外部傳入依賴,將依賴的創(chuàng)建由主動(dòng)變?yōu)楸粍?dòng) (實(shí)現(xiàn)了控制反轉(zhuǎn))。

光說(shuō)理論有點(diǎn)不好理解,我們用代碼舉個(gè)例子。

首先,我們看依賴沒(méi)有倒置時(shí)的一段代碼:

class Controller
{
    protected $service;

    public function __construct()
    {
        // 主動(dòng)創(chuàng)建依賴
        $this->service = new Service(12, 13); 
    }       
}

class Service
{
    protected $model;
    protected $count;

    public function __construct($param1, $param2)
    {
        $this->count = $param1 + $param2;
        // 主動(dòng)創(chuàng)建依賴
        $this->model = new Model("test_table"); 
    }
}

class Model
{
    protected $table;

    public function __construct($table)
    {
        $this->table = $table;
    }
}

$controller = new Controller;

上述代碼的依賴關(guān)系是 Controller 依賴 Service,Service 依賴 Model。從控制的角度來(lái)看,Controller 主動(dòng)創(chuàng)建依賴 Service,Service 主動(dòng)創(chuàng)建依賴 Model。依賴是由需求方內(nèi)部產(chǎn)生的,需求方需要關(guān)心依賴的具體實(shí)現(xiàn)。這樣的設(shè)計(jì)使代碼耦合性變高,每次底層發(fā)生改變(如參數(shù)變動(dòng)),頂層就必須修改代碼。

接下來(lái),我們使用依賴注入實(shí)現(xiàn)控制反轉(zhuǎn),使依賴關(guān)系倒置:

class Controller
{
    protected $service;
    // 依賴被動(dòng)傳入。申明要 Service 類(lèi)的實(shí)例 (抽象接口)
    public function __construct(Service $service)
    {
        $this->service = $service; 
    }       
}

class Service
{
    protected $model;
    protected $count;
    // 依賴被動(dòng)傳入
    public function __construct(Model $model, $param1, $param2)
    {
        $this->count = $param1 + $param2;
        $this->model = $model; 
    }
}

class Model
{
    protected $table;

    public function __construct($table)
    {
        $this->table = $table;
    }
}

$model = new Model("test_table");
$service = new Service($model, 12, 13);
$controller = new Controller($service);

將依賴通過(guò)參數(shù)的方式從外部傳入(即依賴注入),控制的角度上依賴的產(chǎn)生從主動(dòng)創(chuàng)建變?yōu)楸粍?dòng)注入,依賴關(guān)系變?yōu)榱艘蕾囉诔橄蠼涌诙灰蕾囉诰唧w實(shí)現(xiàn)。此時(shí)的代碼得到了解耦,提高了可維護(hù)性。

從單元測(cè)試的角度看,依賴注入更方便 stub 和 mock 操作,方便了測(cè)試人員寫(xiě)出質(zhì)量更高的測(cè)試代碼。

如何依賴注入,自動(dòng)注入依賴

有了上面的一些理論基礎(chǔ),我們大致了解了依賴注入是什么,能干什么。

不過(guò)雖然上面的代碼可以進(jìn)行依賴注入了,但是依賴還是需要手動(dòng)創(chuàng)建。我們可不可以創(chuàng)建一個(gè)工廠類(lèi),用來(lái)幫我們進(jìn)行自動(dòng)依賴注入呢?OK,我們需要一個(gè) IOC 容器。

實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 IOC 容器

依賴注入是以構(gòu)造函數(shù)參數(shù)的形式傳入的,想要自動(dòng)注入:

我們需要知道需求方需要哪些依賴,使用反射來(lái)獲得

只有類(lèi)的實(shí)例會(huì)被注入,其它參數(shù)不受影響

如何自動(dòng)進(jìn)行注入呢?當(dāng)然是 PHP 自帶的反射功能!

注:關(guān)于反射是否影響性能,答案是肯定的。但是相比數(shù)據(jù)庫(kù)連接、網(wǎng)絡(luò)請(qǐng)求的時(shí)延,反射帶來(lái)的性能問(wèn)題在絕大多數(shù)情況下并不會(huì)成為應(yīng)用的性能瓶頸。
1.雛形

首先,創(chuàng)建 Container 類(lèi),getInstance 方法:

class Container
{
    public static function getInstance($class_name, $params = [])
    {
        // 獲取反射實(shí)例
        $reflector = new ReflectionClass($class_name);
        // 獲取反射實(shí)例的構(gòu)造方法
        $constructor = $reflector->getConstructor();
        // 獲取反射實(shí)例構(gòu)造方法的形參
        $di_params = [];
        if ($constructor) {
            foreach ($constructor->getParameters() as $param) {
                $class = $param->getClass();
                if ($class) { // 如果參數(shù)是一個(gè)類(lèi),創(chuàng)建實(shí)例
                    $di_params[] = new $class->name;
                }
            }
        }
        
        $di_params = array_merge($di_params, $params);
        // 創(chuàng)建實(shí)例
        return $reflector->newInstanceArgs($di_params);
    }
}

這里我們獲取構(gòu)造方法參數(shù)時(shí)用到了 ReflectionClass 類(lèi),大家可以到官方文檔了解一下該類(lèi)包含的方法和用法,這里就不再贅述。

ok,有了 getInstance 方法,我們可以試一下自動(dòng)注入依賴了:

class A
{
    public $count = 100;
}

class B
{
    protected $count = 1;

    public function __construct(A $a, $count)
    {
        $this->count = $a->count + $count;
    }

    public function getCount()
    {
        return $this->count;
    }
}

$b = Container::getInstance(B::class, [10]);
var_dump($b->getCount()); // result is 110
2.進(jìn)階

雖然上面的代碼可以進(jìn)行自動(dòng)依賴注入了,但是問(wèn)題是只能構(gòu)注入一層。如果 A 類(lèi)也有依賴怎么辦呢?

ok,我們需要修改一下代碼:

class Container
{
    public static function getInstance($class_name, $params = [])
    {
        // 獲取反射實(shí)例
        $reflector = new ReflectionClass($class_name);
        // 獲取反射實(shí)例的構(gòu)造方法
        $constructor = $reflector->getConstructor();
        // 獲取反射實(shí)例構(gòu)造方法的形參
        $di_params = [];
        if ($constructor) {
            foreach ($constructor->getParameters() as $param) {
                $class = $param->getClass();
                if ($class) { // 如果參數(shù)是一個(gè)類(lèi),創(chuàng)建實(shí)例,并對(duì)實(shí)例進(jìn)行依賴注入
                    $di_params[] = self::getInstance($class->name);
                }
            }
        }
        
        $di_params = array_merge($di_params, $params);
        // 創(chuàng)建實(shí)例
        return $reflector->newInstanceArgs($di_params);
    }
}

測(cè)試一下:

class C 
{
    public $count = 20;
}
class A
{
    public $count = 100;

    public function __construct(C $c)
    {
        $this->count += $c->count;
    }
}

class B
{
    protected $count = 1;

    public function __construct(A $a, $count)
    {
        $this->count = $a->count + $count;
    }

    public function getCount()
    {
        return $this->count;
    }
}

$b = Container::getInstance(B::class, [10]);
var_dump($b->getCount()); // result is 130
上述代碼使用遞歸完成了多層依賴的注入關(guān)系,程序中依賴關(guān)系層級(jí)一般不會(huì)特別深,遞歸不會(huì)造成內(nèi)存遺漏問(wèn)題。
3.單例

有些類(lèi)會(huì)貫穿在程序生命周期中被頻繁使用,為了在依賴注入中避免不停的產(chǎn)生新的實(shí)例,我們需要 IOC 容器支持單例模式,已經(jīng)是單例的依賴可以直接獲取,節(jié)省資源。

為 Container 增加單例相關(guān)方法:

class Container
{
    protected static $_singleton = []; 

    // 添加一個(gè)實(shí)例到單例
    public static function singleton($instance)
    {
        if ( ! is_object($instance)) {
            throw new InvalidArgumentException("Object need!");
        }
        $class_name = get_class($instance);
        // singleton not exist, create
        if ( ! array_key_exists($class_name, self::$_singleton)) {
            self::$_singleton[$class_name] = $instance;
        }
    }
    // 獲取一個(gè)單例實(shí)例
    public static function getSingleton($class_name)
    {
        return array_key_exists($class_name, self::$_singleton) ?
                self::$_singleton[$class_name] : NULL;
    }
    // 銷(xiāo)毀一個(gè)單例實(shí)例
    public static function unsetSingleton($class_name)
    {
        self::$_singleton[$class_name] = NULL;
    }

}

改造 getInstance 方法:

public static function getInstance($class_name, $params = [])
{
    // 獲取反射實(shí)例
    $reflector = new ReflectionClass($class_name);
    // 獲取反射實(shí)例的構(gòu)造方法
    $constructor = $reflector->getConstructor();
    // 獲取反射實(shí)例構(gòu)造方法的形參
    $di_params = [];
    if ($constructor) {
        foreach ($constructor->getParameters() as $param) {
            $class = $param->getClass();
            if ($class) { 
                // 如果依賴是單例,則直接獲取
                $singleton = self::getSingleton($class->name);
                $di_params[] = $singleton ? $singleton : self::getInstance($class->name);
            }
        }
    }
    
    $di_params = array_merge($di_params, $params);
    // 創(chuàng)建實(shí)例
    return $reflector->newInstanceArgs($di_params);
}
4.以依賴注入的方式運(yùn)行方法

類(lèi)之間的依賴注入解決了,我們還需要一個(gè)以依賴注入的方式運(yùn)行方法的功能,可以注入任意方法的依賴。這個(gè)功能在實(shí)現(xiàn)路由分發(fā)到控制器方法時(shí)很有用。

增加 run 方法

public static function run($class_name, $method, $params = [], $construct_params = [])
{
    if ( ! class_exists($class_name)) {
        throw new BadMethodCallException("Class $class_name is not found!");
    }

    if ( ! method_exists($class_name, $method)) {
        throw new BadMethodCallException("undefined method $method in $class_name !");
    }
    // 獲取實(shí)例
    $instance = self::getInstance($class_name, $construct_params);

    // 獲取反射實(shí)例
    $reflector = new ReflectionClass($class_name);
    // 獲取方法
    $reflectorMethod = $reflector->getMethod($method);
    // 查找方法的參數(shù)
    $di_params = [];
    foreach ($reflectorMethod->getParameters() as $param) {
        $class = $param->getClass();
        if ($class) { 
            $singleton = self::getSingleton($class->name);
            $di_params[] = $singleton ? $singleton : self::getInstance($class->name);
        }
    }

    // 運(yùn)行方法
    return call_user_func_array([$instance, $method], array_merge($di_params, $params));
}

測(cè)試:

class A
{
    public $count = 10;
}

class B
{
    public function getCount(A $a, $count)
    {
        return $a->count + $count;
    }
}

$result = Container::run(B::class, "getCount", [10]);
var_dump($result); // result is 20

ok,一個(gè)簡(jiǎn)單好用的 IOC 容器完成了,動(dòng)手試試吧!

完整代碼

IOC Container 的完整代碼請(qǐng)見(jiàn) wazsmwazsm/IOCContainer, 原先是在我的框架 wazsmwazsm/WorkerA 中使用,現(xiàn)在已經(jīng)作為多帶帶的項(xiàng)目,有完善的單元測(cè)試,可以使用到生產(chǎn)環(huán)境。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/31304.html

相關(guān)文章

  • 詳解 Laravel 中的依賴注入IoC

    摘要:依賴注入依賴注入一詞是由提出的術(shù)語(yǔ),它是將組件注入到應(yīng)用程序中的一種行為。就像說(shuō)的依賴注入是敏捷架構(gòu)中關(guān)鍵元素。類(lèi)依賴于,所以我們的代碼可能是這樣的創(chuàng)建一個(gè)這是一種經(jīng)典的方法,讓我們從使用構(gòu)造函數(shù)注入開(kāi)始。 showImg(https://segmentfault.com/img/remote/1460000018806800); 文章轉(zhuǎn)自:https://learnku.com/la...

    haitiancoder 評(píng)論0 收藏0
  • 手寫(xiě)Spring之DI依賴注入

    摘要:如感興趣,可移步手寫(xiě)之基于動(dòng)態(tài)創(chuàng)建對(duì)象手寫(xiě)之基于注解動(dòng)態(tài)創(chuàng)建對(duì)象今天將詳細(xì)介紹如何手寫(xiě)依賴注入,在運(yùn)行過(guò)程中如何動(dòng)態(tài)地為對(duì)象的屬性賦值。完成后在中會(huì)有相關(guān)的包出現(xiàn)進(jìn)行注入前需要?jiǎng)?chuàng)建工廠,在運(yùn)行時(shí)從工廠中取出對(duì)象為屬性賦值。 前兩篇文章介紹了關(guān)于手寫(xiě)Spring IOC控制反轉(zhuǎn),由Spring工廠在運(yùn)行過(guò)程中動(dòng)態(tài)地創(chuàng)建對(duì)象的兩種方式。如感興趣,可移步: 手寫(xiě)Spring之IOC基于xml...

    Cruise_Chan 評(píng)論0 收藏0
  • 聊一聊PHP依賴注入(DI) 和 控制反轉(zhuǎn)(IoC)

    摘要:前言最近在使用框架,看了下他的源碼,發(fā)現(xiàn)有很多地方也用到了依賴注入控制反轉(zhuǎn),覺(jué)得有必要和大家簡(jiǎn)單聊一聊什么是依賴注入以及怎么使用它。概念依賴注入和控制反轉(zhuǎn)是對(duì)同一件事情的不同描述,從某個(gè)方面講,就是它們描述的角度不同。 前言 最近在使用ThinkPHP5框架,看了下他的源碼,發(fā)現(xiàn)有很多地方也用到了依賴注入(控制反轉(zhuǎn)),覺(jué)得有必要和大家簡(jiǎn)單聊一聊什么是依賴注入以及怎么使用它。 簡(jiǎn)介 I...

    sixgo 評(píng)論0 收藏0
  • PHP IOC/DI 容器 - 依賴自動(dòng)注入/依賴單例注入/依賴契約注入/參數(shù)關(guān)聯(lián)傳值

    摘要:標(biāo)量參數(shù)關(guān)聯(lián)傳值依賴是自動(dòng)解析注入的,剩余的標(biāo)量參數(shù)則可以通過(guò)關(guān)聯(lián)傳值,這樣比較靈活,沒(méi)必要把默認(rèn)值的參數(shù)放在函數(shù)參數(shù)最尾部。 更新:github(給個(gè)小星星呀) -- 2018-4-11:優(yōu)化服務(wù)綁定方法 ::bind 的類(lèi)型檢查模式 借助 PHP 反射機(jī)制實(shí)現(xiàn)的一套 依賴自動(dòng)解析注入 的 IOC/DI 容器,可以作為 Web MVC 框架 的應(yīng)用容器 1、依賴的自動(dòng)注入:你只需要...

    Paul_King 評(píng)論0 收藏0
  • Spring IOC知識(shí)點(diǎn)一網(wǎng)打盡!

    摘要:使用的好處知乎的回答不用自己組裝,拿來(lái)就用。統(tǒng)一配置,便于修改。 前言 只有光頭才能變強(qiáng) 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡(jiǎn)單啦 單例模式你會(huì)幾種寫(xiě)法? 工廠模式理解了沒(méi)有? 在刷Spring書(shū)籍的時(shí)候花了點(diǎn)時(shí)間去學(xué)習(xí)了單例模式和工廠模式,總的來(lái)說(shuō)還是非常值得的! 本來(lái)想的是刷完《Spring 實(shí)戰(zhàn) (第4版)》和《精通Spring4.x 企業(yè)應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)》...

    djfml 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<