PHP依賴注入(DI)和控制反轉(IOC)

這2個其實都算得上是一種設計模式或者說是一種軟件設計思想,目的都是爲了增長軟件可維護性和擴展性,在不少框架,好比Java Web框架SpringMVC 和PHP Web框架laravel裏面都有應用。php

首先得理解什麼叫依賴?從宏觀上看,得益於開源軟件運行的興起,不少時候咱們寫項目並非什麼都是從零開始,咱們每每會利用不少現成的開源代碼進行快速開發,能不重複造輪子最好,因此咱們每每依賴不少開源組件。gradle、npm、composer 等工具的部分功能就是解決項目依賴問題。laravel

從微觀上看,在實際寫代碼裏面,對象與對象之間也會產生依賴關係,好比一個數據庫查詢類須要用到一個數據庫鏈接、一個文章評論類用到一個文章,這裏的依賴主要指對象之間的關係。redis

舉個栗子,在一個 SessionService 裏面你須要一個 FileSession :數據庫

普通寫法:

class FileSession {
    private $file;

    ... more code

    public function set($name, $value) {
        echo "set $name = $value into $this->file\n";
    }

    public function get($name) {
        echo "get $name value\n";
    }
}
複製代碼

service類:npm

class SessionService {
    private $sessionHandler;

    public function __construct() {
        $this->sessionHandler = new FileSession();
    }

    public function set($name, $value) {
        $this->sessionHandler->set($name, $value);
    }

    public function get($name) {
        return $this->sessionHandler->get($name);
    }

    ...more code
}
複製代碼

在這種普通寫法裏面,當咱們須要一個 sessionHandler 的時候咱們是直接在構造函數裏面實例化,這樣沒啥問題,確實解決了依賴問題。可是依賴注入的另外一個詞「注入」更強調的是一種從外部而來的,而不是內部。設計模式

改造以下:緩存

依賴注入寫法:

class SessionService {
    private $sessionHandler;

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

    public function set($name, $value) {
        $this->sessionHandler->set($name, $value);
    }

    public function get($name) {
        return $this->sessionHandler->get($name);
    }

    ...more code
}
複製代碼

這種寫法要求你在使用service的時候從外部傳入一個handler,這就實現了依賴注入,注入的方式有不少種,剛纔這種能夠稱之爲構造器注入,還有一種叫setter注入,好比說,咱們能夠在service裏面裏面提供一個setter函數用於設置所需的handler:網絡

public function setSessionHandler($sessionHandler) {
    $this->sessionHandler = $sessionHandler
}
複製代碼

這種寫法有哪些好處呢?一個是解耦,假如說這個FileSession實例化的時候還須要其它操做,好比傳入一個配置參數,本來的寫法可能就須要更改service類了,在構造函數裏面啪啪啪寫一堆。還有就是方便測試,既然解耦了就能夠很方便的進行單元測試。另外一個是控制反轉,就是說這個FileSession外部傳入的,是service類沒法控制的,也就說控制權在於外部。session

不少軟件在設計的時候都採用分層結構,最典型的就是計算機網絡,Http協議依賴TCP協議,層與層之間經過約定的的接口進行交互,既減小了代碼的複雜度,也提升了可維護性。好比說你哪一天重構了FileSession,沒問題,只要你保證全部方法的返回結果和以前同樣就行。composer

爲了更靈活的運用這種注入機制咱們可能須要採用一個接口去約束,舉個例子,咱們先增長一個接口sessionHandler:

interface SessionHandler {
    public function set($name, $value);

    public function get($name);
}
複製代碼

咱們約定,只要你實現了這個接口,你就能夠當一個sessionHandler,你就能夠用來處理session,至於你怎麼實現,service無論,好比說咱們換一個redis:

class RedisHandler implments SessionHandler {
    private $redisInstance;

    public function __construct() {
        $this->redisInstance = new Redis();
    }
    public function set($name, $value) {
        $this->redisInstance->set($name, $value);
    }

    public function get($name) {
        return $this->redisInstance->get($name);
    }
}
複製代碼

這時候咱們能夠在service的構造函數稍做修改,增長一個類型約束:

public function __construct(SessionHandler $sessionHandler) {
        $this->sessionHandler = $sessionHandler;
 }
複製代碼

這樣的設計以後,好處顯而易見,咱們能夠很輕鬆替換掉以前的fileSession,不改動service的一行代碼,只要按照sessionHandler的接口去實現相應的方法就行,在laravel裏面這樣的接口就叫作 Contracts,下面就是框架裏面的Cache緩存的 Contracts:

<?php

namespace Illuminate\Contracts\Cache;

interface Store {
    /** * Retrieve an item from the cache by key. * * @param string|array $key * @return mixed */
    public function get($key);

    /** * Retrieve multiple items from the cache by key. * * Items not found in the cache will have a null value. * * @param array $keys * @return array */
    public function many(array $keys);

    /** * Store an item in the cache for a given number of minutes. * * @param string $key * @param mixed $value * @param float|int $minutes * @return void */
    public function put($key, $value, $minutes);

    /** * Store multiple items in the cache for a given number of minutes. * * @param array $values * @param float|int $minutes * @return void */
    public function putMany(array $values, $minutes);

    ... more code
}
複製代碼

據我看到的,在laravel框架裏面自帶了至少5種實現,分別是Array、File、Database、Memcached、Redis, 若是你願意你也能夠本身去實現這個 Contracts,而後替換到框架裏面的,不過框架自己實現的已經很是優秀了,除非你寫的更好,通常狀況下不須要這樣作,可是laravel提供了這種可能。 一樣,在laravel框架裏面session自帶了Cache,Database,File這種幾種實現,能夠隨意切換。

session


IOC容器

說了最後,必須再說說IOC容器,IOC核心思想是經過IoC容器管理對象的生成、資源獲取、銷燬等生命週期,在IoC容器中創建對象與對象之間的依賴關係,IoC容器啓動後,全部對象直接取用,調用層再也不使用new操做符產生對象和創建對象之間的依賴關係。

簡單理解就是再也不使用new建立對象了,並且使用容器來管理對象,須要對象就從容器裏面取,並且你只須要在參數上聲明依賴,容器就直接給你對象了,炒雞方便,好比在laravel裏面,有不少這樣的寫法:

public function comment(Post $post, Request $request) {
    $this->validate($request, [
        'content' => 'required|min:5'
    ]);

    $comment = new Comment([
        'content' => $request->get('content'),
        'user_id' => auth()->user()->id,
        'post_id' => $post->id,
    ]);

    $post->comments()->save($comment);

    return redirect()->back();
}
複製代碼

咱們只須要在方法的參數上面標明所需的方法,就能夠在代碼直接用了,ioc容器替咱們自動注入了依賴!

相關文章
相關標籤/搜索