溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

php依賴注入

發(fā)布時間:2020-07-25 10:29:52 來源:網(wǎng)絡(luò) 閱讀:1213 作者:china_lx1 欄目:開發(fā)技術(shù)

在軟件工程領(lǐng)域,依賴注入(Dependency Injection)是用于實現(xiàn)控制反轉(zhuǎn)(Inversion of Control)的最常見的方式之一。本文主要介紹依賴注入原理和常見的實現(xiàn)方式,重點在于介紹這種年輕的設(shè)計模式的適用場景及優(yōu)勢。


首先我們來一個實例,上代碼

<?php

class A
{
	public function test()
	{
		echo 'this is A!<br>';
		$b = new B();
		$b->test();
	}
}

class B
{
	public function test()
	{
		echo 'this is B!<br>';
		$c = new C();
		$c->test();
	}
}

class C
{
	public function test()
	{
		echo 'this is C!<br>';
	}
}

$obj = new A();

$obj->test();


結(jié)果是:

this is A!
this is B!
this is C!


從代碼分析,A類依賴B類,B類依賴C類。這是我們最原始的實現(xiàn)思路.這種實現(xiàn)思路很明顯會有問題

假如我們現(xiàn)在B類修改下,代碼如下:

class B
{

	public $name;

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

	public function test()
	{
		echo 'this is B'.$this->name.'!<br>';
		$c = new C();
		$c->test();
	}
}


此時再看我們原來A類test方法直接調(diào)用明顯會有問題,于是此時我們需要將原來代碼:

class A
{
	public function test()
	{
		echo 'this is A!<br>';
		$b = new B();
		$b->test();
	}
}

修改成:

class A
{
	public function test()
	{
		echo 'this is A!<br>';
		$b = new B('(class B)');//構(gòu)造的時候多加了一個參數(shù)
		$b->test();
	}
}

如果此時C類構(gòu)造方法也變動了呢,B的方法里面也同樣受影響,很明顯可用性非常低


為了解耦,此時我們用到第二種思路:構(gòu)造器注入,直接上代碼:

<?php

class A
{
	public $obj;

	public function __construct(B $b)
	{
		$this->obj = $b;
	}

	public function test()
	{
		echo 'this is A!<br>';
		$this->obj->test();
	}
}

class B
{
	public $obj;
	public function __construct(C $c)
	{
		$this->obj = $c;
	}

	public function test()
	{
		echo 'this is B!<br>';
		$this->obj->test();
	}
}

class C
{
	public function test()
	{
		echo 'this is C!<br>';
	}
}
$c = new C();
$b = new B($c);
$obj = new A($b);
$obj->test();

這種方法可以解決第一種方法,如果依賴的類構(gòu)造方法(比如B類)有所改動,A類不需要改動任何代碼。但是久而久之,這種方法毛病也出來了,我們首先必須要先實例化C類,再實例化B類,最后再實例化A類。


了解到第二種方案需要優(yōu)化,于是出現(xiàn)了基本成型的第三種方案,此時我們用到了container(容器)和instance(實例),直接上代碼:

class Container
{

	public static $_definations = [];
	
	
	public function get($class)
	{

		//當前類所依賴的類
		$depends = [];
		
		$tc = new ReflectionClass($class);
		//得到構(gòu)造方法
		$constructor = $tc->getConstructor();

		//得到構(gòu)造方法的參數(shù)
		if($constructor !== NULL)
		{
			foreach($constructor->getParameters() as $parameter)
			{
				if($parameter->isDefaultValueAvailable())
				{
					$depends[] = $parameter->getDefaultValue();
				}
				else
				{
					$pc = $parameter->getClass();
					$instance = Instance::getInstance($pc == NULL ? NULL : $pc->getName());
					$depends[] = $instance;
				}
			}
		}

		foreach($depends as $k => $v)
		{
			if($v instanceof Instance)
			{
				if($v->id !== NULL)
				{
					$depends[$k] = $this->get($v->id);
				}
			}
		}

		$tm_instance = $tc->newInstanceArgs($depends);
		return $tm_instance;
	}
}

class Instance{
	/**
	 * @var 類唯一標示
	 */
	public $id;

	/**
	 * 構(gòu)造函數(shù)
	 * @param string $id 類唯一ID
	 * @return void
	 */
	public function __construct($id)
	{
		$this->id = $id;
	}

	/**
	 * 獲取類的實例
	 * @param string $id 類唯一ID
	 * @return Object Instance
	 */
	public static function getInstance($id)
	{
		return new self($id);
	}
}

class Base{
	
}

class A extends Base{
	private $instanceB;

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

	public function test()
	{
		echo 'this is A!<br/>';
		$this->instanceB->test();
	}
}


class B  extends Base{
	private $instanceC;

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

	public function test()
	{
		echo 'this is B!<br/>';
		return $this->instanceC->test();
	}
}

class C  extends Base{
	public function test()
	{
		echo 'this is C!';
	}
}


$container = new Container();

$obj_a = $container->get('A');

$obj_a->test();

此方法有參考yii2中yii2\di\container實現(xiàn),只是將它簡化了,更容易看懂。重點看看container的get方法


現(xiàn)在我們增加set方法,自定義函數(shù),直接上代碼精簡版

class Container
{
	/**
	*@var array 存儲各個類的定義  以類的名稱為鍵
	*/
	public static $_definations = [];

	
	public function get($class, $params = [], $props = [])
	{
		if(!isset(self::$_definations[$class]))
			return $this->build($class, $params, $props);

		//如果已經(jīng)被定義過的
		$_defination = self::$_definations[$class];
		
		if(is_callable($_defination, true))
		{
			return call_user_func($_defination, $this, $params, $props);
		}
		else if(is_object($_defination))
		{
			return $_defination;
		}
		else
		{
			throw new Exception($class . '聲明錯誤');
		}
	}

	public function set($class, $_defination)
	{
		self::$_definations[$class] = $_defination;
		
	}

	public function build($class, $params, $props)
	{
		//當前類所依賴的類
		$depends = [];
		
		$tc = new ReflectionClass($class);
		//得到構(gòu)造方法
		$constructor = $tc->getConstructor();

		//得到構(gòu)造方法的參數(shù)
		if($constructor !== NULL)
		{
			foreach($constructor->getParameters() as $parameter)
			{
				if($parameter->isDefaultValueAvailable())
				{
					$depends[] = $parameter->getDefaultValue();
				}
				else
				{
					$pc = $parameter->getClass();
					$instance = Instance::getInstance($pc == NULL ? NULL : $pc->getName());
					$depends[] = $instance;
				}
			}
		}

		foreach($depends as $k => $v)
		{
			if($v instanceof Instance)
			{
				if($v->id !== NULL)
				{
					$depends[$k] = $this->get($v->id);
				}
			}
		}

		$tm_instance = $tc->newInstanceArgs($depends);
		return $tm_instance;
	}
}


增加了一個set方法,并將get方法稍微改版了一下,現(xiàn)在我們看看怎么調(diào)用set方法:

$container = new Container();

$container->set('foo', function($container, $params, $config){
	print_r($params);
	print_r($config);
});

$container->get('foo', ['p1' => 'pv1'], ['c1' => 'cv1']);


輸出結(jié)果為:

Array ( [p1] => pv1 ) Array ( [c1] => cv1 )


再看看另外一種情況調(diào)用:

class Test
{
    public function mytest()
    {
        echo 'this is a test';
    }
}

$container->set('testObj', new Test());

$test = $container->get('testObj');
$test->mytest();


輸出結(jié)果為:

this is a test


上面的代碼只是作者粗略的寫了下簡單版本,很多地方不是太完善,這版本是為了理解yii2的container打下基礎(chǔ),稍后會出yii2的依賴注入源碼分析

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI