首頁 > 後端開發 > php教程 > 一文理解與實現現代PHP框架裡的IOC容器

一文理解與實現現代PHP框架裡的IOC容器

發布: 2023-04-11 09:30:01
轉載
4795 人瀏覽過

這篇文章為大家帶來了關於PHP的相關知識,其中主要介紹了關於IOC容器的相關內容,IOC的全名是:Inversion Of Control,反轉控制,下面一起來看一下,希望對大家有幫助。

一文理解與實現現代PHP框架裡的IOC容器

容器是什麼?

相信很多人聽過依賴注入,依賴注入實作的基礎條件離不開容器,容器就是用來管理類別依賴和注入的,負責服務的管理和解耦元件,最簡單的理解我們可以把容器理解成一個超級大、專門存物件的陣列。

一文理解與實現現代PHP框架裡的IOC容器

如圖所示呼叫者透過容器的標示取得到物件實例,圖裡可以看出來,可以透過::class 的方式來取得也可以直接透過對象標示取得實例物件。

IOC是什麼?

大家可能都聽過IOC容器,IOC的全名是:(Inversion Of Control,反轉控制)。

我們來理解什麼是反轉控制,在我們傳統編碼中我們在類別與類別之間的依賴通常是我們透過編碼的方式new出來物件再傳遞的,而使用控制反轉我們可以把物件的控制權交給容器或是框架去實現。目的是為了讓我們不需要硬編碼去創建對象,看圖1可以知道,容器裡面存放著很多對象,當我們要使用的時候可以直接去用。而容器裡面的物件不需要我們在程式碼中編碼創建。在需要某個類別對象的時候會去容器裡面取得對象,如果對像不存在則會自動建立。這就是省略了我們在程式碼裡面去創建物件的過程,由容器去幫我們實現這個創建的過程,這就叫反轉控制。一句話總結IOC:把創建物件的控制權轉移給容器實作類別的實例化。

例如:沒有使用IOC的情況下,我們想要建立類別

<?php
class Sunny{
}
$sunny = new Sunny();
登入後複製

我們需要手動去new一個類,這種情況就是硬編碼在程式碼裡面去實現的。

而使用IOC容器的程式碼則可以這樣寫。

<?php
class Sunny{
}
$sunny = Container::getBean(Sunny::class);
登入後複製

在容器的內部去幫我們實作這個類,有同學看到這裡可能會有疑問,我使用 new Sunny 不是程式碼寫得更短更簡單嗎?我們看完依賴注入再看一個例子。

依賴注入

現在知道了IOC是什麼,那麼一個新的問題出來了,我們在創建類別的時候有些類別的建構方法會需要我們傳遞參數怎麼辦?透過IOC的學習我們知道了IOC容器會幫我們解決這個物件實例創建的問題,那麼在容器裡面創建物件的時候發現類別有其他依賴則會進行依賴查找,容器尋找需要物件的過程,稱為DL( Dependency Lookup, 依賴查找)。而把需要的依賴注入到程式碼片段中這個稱為DI(Dependency Injection,依賴注入)。

例如IOC裡面說到的 new Sunny 這個例子。如果在類別與類別之間有多重依賴。

<?php
class Computer{
    public function run(){
        echo "编程中....\n";
    }
}
class Sunny{
    private $computer;
    public function __construct(Computer $computer){
        $this->computer = $computer;
    }
    public function program(){
        $this->computer->run();
    }
}
$sunny = new Sunny(new Computer());
$sunny->program();
登入後複製

這裡可以看到 Sunny 這個類別想要程式依賴類別 Computer 這個類,而如果使用IOC容器實作依賴注入的話,程式碼就簡單了。

<?php
class Computer{
    public function run(){
        echo "编程中....\n";
    }
}
class Sunny{
    private $computer;
    public function __construct(Computer $computer){
        $this->computer = $computer;
    }
    public function program(){
        $this->computer->run();
    }
}
$sunny = Container::getBean(Sunny::class);
$sunny->program();
登入後複製

一句話總結:解決創建類別實例當中對其他類別的依賴,動態的向某個物件提供它所需要的其他物件。

依賴倒置

依賴倒置解決的問題是鬆耦各個模組之間的重度依賴,上層模組不應該依賴底層模組,它們都應該依賴抽象。通常簡單的理解依賴倒置就是面向介面或面向抽象來進行程式設計。我們透過下面的例子來看看面向介面程式設計。

class Cache{
    public function set($key,$value){
        $redis = new CFile();
        $redis->set($key,$value);
    }
}
class CFile{
    public function set($key,$value){
        echo "file:{$key}->{$value}\n";
    }
}
$cache = new Cache();
$cache->set("name","sunny");
登入後複製

上面的這段程式碼看似沒有什麼大問題,但是如果有一天把檔案快取改成Redis快取呢?

class Cache{
    public function set($key,$value){
        $redis = new CRedis();
        $redis->set($key,$value);
    }
}
class CRedis{
    public function set($key,$value){
        echo "redis:{$key}->{$value}\n";
    }
}
$cache = new Cache();
$cache->set("name","sunny");
登入後複製

透過這段程式碼可以看出來當一個快取使用的驅動改變了的時候,Cache的程式碼也必須做出對應的改變,因為程式碼寫死在呼叫者身上了,耦合度變得高了。再將程式碼改造一樣,讓程式設計師面向interface編程,讓程式碼變得更通用,更規範。

interface ICache{
    public function set($key,$value);
}
class CRedis implements ICache {
    public function set($key,$value)
{
        echo "redis:{$key}->{$value}\n";
    }
}
class CFile implements ICache{
    public function set($key,$value)
{
        echo "file:{$key}->{$value}\n";
    }
}
class Cache{
    private $drive;
    public function __construct(ICache $drive)
{
        $this->drive = $drive;
    }
    public function set($key,$value){
        $this->drive->set($key,$value);
    }
}
$cache = new Cache(new CFile());
$cache->set("name","sunny");
登入後複製

很多人看到這段程式碼的時候想著,那我在建構方法直接把要的物件傳進去不就好了嗎?為什麼還要定義一個interface呢?其實定義interface是為了規範程式碼,不管你使用哪個驅動,只要實現了我這個interface的都可以用,沒有interface開發者在開發驅動的時候就會不知道這個驅動裡面該有什麼方法。當我們使用interface之後大家只要面向介面編程,Cache完全不管類別是怎麼實現的,Cache只是依照interface的方法進行操作。

一句話總結:依賴倒實現鬆散耦合

實戰:根據容器原理實作容器

<?php
class Container
{
    // 当前容器对象
    private static $instance;
    // 存放在容器里面到实例
    protected $instances = [];
    private function __construct()
{
    }
    public static function getInstance()
{
        if (!self::$instance) {
            self::$instance = new static();
        }
        return self::$instance;
    }
    /**
     * 获取对象实例
     * @param $key
     * @return mixed
     */
    public function get($key)
{
        if (isset($this->instances[$key])) {
            return $this->instances[$key];
        }
    }
    /**
     * 绑定对象、闭包、类到容器
     * @param $key
     * @param null $concrete
     * @return Container
     */
    public function bind($key, $concrete = null)
{
        if ($concrete instanceof Closure) {
            $this->instances[$key] = $concrete;
        } elseif (is_object($concrete)) {
            $this->instances[$key] = $concrete;
        }
        return $this;
    }
}
class Sunny
{
    public function getName()
{
        echo time() . "\n";
    }
}
$app = Container::getInstance();
$sunny = $app->bind(Sunny::class,new Sunny());
$sunny = $app->get(Sunny::class);
$sunny->getName();
登入後複製

實戰:實作依賴注入

Container.php
<?php
class Container
{
    // 当前容器对象
    private static $instance;
    // 存放在容器里面到实例
    protected $instances = [];
    private function __construct()
{
    }
    public static function getInstance()
{
        if (!self::$instance) {
            self::$instance = new static();
        }
        return self::$instance;
    }
    /**
     * 获取对象实例
     * @param $key
     * @return mixed
     * @throws ReflectionException
     */
    public function get($key)
{
        if (isset($this->instances[$key])) {
            return $this->instances[$key];
        }
        return $this->make($key);
    }
    /**
     * 绑定对象、闭包、类到容器
     * @param $key
     * @param null $concrete
     * @return Container
     * @throws ReflectionException
     */
    public function bind($key, $concrete = null)
{
        if ($concrete instanceof Closure) {
            $this->instances[$key] = $concrete;
        } elseif (is_object($concrete)) {
            $this->instances[$key] = $concrete;
        } else {
            $this->make($key, $concrete);
        }
        return $this;
    }
    /**
     * 创建类绑定到类实例
     * @param $abstract
     * @param null $atgs
     * @return mixed
     * @throws ReflectionException
     */
    public function make($abstract, $atgs = null)
{
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }
        $object = $this->invokeClass($abstract);
        $this->instances[$abstract] = $object;
        return $object;
    }
    /**
     * 反射解析类
     * @param $abstract
     * @return object
     * @throws ReflectionException
     */
    public function invokeClass($abstract)
{
        $reflectionClass = new \ReflectionClass($abstract);
        // 获取构造方法
        $construct = $reflectionClass->getConstructor();
        // 获取参数得到实例
        $params = $construct ? $this->parserParams($construct) : [];
        $object = $reflectionClass->newInstanceArgs($params);
        return $object;
    }
    /**
     * 解析构造方法参数
     * @param $reflect
     * @return array
     * @throws ReflectionException
     */
    public function parserParams(ReflectionMethod $reflect)
{
        $args = [];
        $params = $reflect->getParameters();
        if (!$params) {
            return $args;
        }
        if (count($params) > 0) {
            foreach ($params as $param) {
                $class = $param->getClass();
                if ($class) {
                    $args[] = $this->make($class->getName());
                    continue;
                }
                // 获取变量的名称
                $name = $param->getName();
                // 默认值
                $def = null;
                // 如果有默认值,从默认值获取类型
                if ($param->isOptional()) {
                    $def = $param->getDefaultValue();
                }
                $args[] = $_REQUEST[$name] ?? $def;
            }
        }
        return $args;
    }
}
Test.php
<?php
class Test
{
    public $name;
    private $test1;
    public function __construct(Test1 $test1)
{
        $this->test1 = $test1;
        $this->name = $this->test1->getName();
    }
}
Test1.php
<?php
class Test1
{
    public function getName(){
        return "test1返回的名字";
    }
}
Sunny.php
<?php
require_once "./Container.php";
require_once "./Test.php";
require_once "./Test1.php";
class Sunny
{
    private $test;
    public function __construct(Test $test)
{
        $this->test = $test;
    }
    public function getName()
{
        echo "获取test里面的name:{$this->test->name}\n";
    }
}
$app = Container::getInstance();
$sunny = $app->get(Sunny::class);
$sunny->getName();
登入後複製

推薦學習: 《PHP影片教學

以上是一文理解與實現現代PHP框架裡的IOC容器的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
php
來源:IT不是挨踢微信公众号
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板