您的位置:首页 > 编程语言 > PHP开发

Laravel框架下容器Container 的依赖注入和反射应用

2017-12-04 14:41 555 查看
  依赖注入,简单说是把类里头依赖的对象,置于类外头,即客户端调用处。相当于把类与类解耦。

  一个简单的例子:

  

class A {

public function __construct() {
// 这种实例化写法, A类的内部,依赖了B类 需要解耦
$this->b = new B();
}

public function say(){
$this->b->talk();
}
}

class B{

public function __construct() {

}

public function talk(){
echo __METHOD__;
}
}

$a = new A;
$a->say();


  A类依赖B类,耦合较重。

  换一种写法:

  

class A {

public function __construct(B $b) {// B的实例化,放在客户端,当作构造参数传递过来
$this->b = $b;
}

public function say(){
$this->b->talk();
}
}

class B{
public function __construct() {
}

public function talk(){
echo __METHOD__;
}
}

$b = new B;// 在客户端实例化B
$a = new A($b);// 构造器中传递
$a->say();


这个代码,就相当于实例化B的步骤放在了A类的外部,从而实现解耦。这就是依赖注入的一种实现方式。

Laravel框架中,通过这种依赖注入的方式,再配合反射功能,实现功能。

比如在Laravel的容器类,Container.php(vendor/laravel/framework/src/illuminate/Container/Container.php)中

利用反射,实例化注入的类。

来一段代码:

/**
*
* 依赖注入的思想,把外部依赖的类放在构造函数的参数里
*
* Laravel框架根据反射 搞定依赖
*/

class Test{

public function __construct(Para $p1, Parb $p2) {
echo $p1->getInfo()," ",$p2->getInfo()," ";
}

function say(){
echo __CLASS__;
return;
}
}

/**
* Class Para
* 参数A
*/
class Para {

function getInfo() {
echo __CLASS__;
}
}

/**
* Class Parb
* 参数B
*/
class Parb {

function getInfo(){
echo __CLASS__;
}
}

$obj = new Test(new Para(), new Parb());

// ===================================================

$reflector = new ReflectionClass($obj);// 对象 反射其类信息
$constructor = $reflector->getConstructor();

$dependencies = $constructor->getParameters();// 获取构造器下的参数信息
$parArr = array();
foreach ($dependencies as $depend){
// $depend->getClass()->name 获取类名称 构造器参数是类
$parArr[] = new ($depend->getClass()->name)();
}

$refNew = $reflector->newInstanceArgs($parArr);// Test 对象

$refNew->say();


代码中,获取$obj反射后的类信息,再取其构造器中的依赖类,实例化这些依赖类,再传入Test类中。  

这就是Laravel容器中实现的依赖注入和反射应用。

下边的代码有助于理解Laravel框架下的容器Container概念。参考自文章:https://www.insp.top/article/learn-laravel-container

<?php

interface SuperModuleInterface{
public function activate(array $target);
}

class Superman
{
protected $module;

/**
* Superman constructor.
* @param SuperModuleInterface $module
* 通过构造器 注入依赖
*/
public function __construct(SuperModuleInterface $module)
{
$this->module = $module;
}

public function show(array $target){

$this->module->activate($target);
}
}

class PowerA implements SuperModuleInterface
{
public function activate(array $target)
{
echo '<pre>'. __METHOD__;
print_r(func_get_args());
}
}

class PowerB implements SuperModuleInterface
{
public function activate(array $target)
{
echo '<pre>'. __METHOD__;
print_r(func_get_args());
}
}

class Container
{
protected $binds;

protected $instances;

/**
* @param $abstract
* @param $concrete
* 把代词 绑定到容器里,为后续make
*/
public function bind($abstract, $concrete)
{
if ($concrete instanceof Closure) {
$this->binds[$abstract] = $concrete;
} else {
$this->instances[$abstract] = $concrete;
}
}

/**
* @param $abstract
* @param array $parameters
* @return mixed
* 创建对象
*/
public function make($abstract, $parameters = [])
{
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
// 把容器对象$this,放到参数数组第一个元素。为call_user_func_array使用
array_unshift($parameters, $this);

// 这里$this->binds[$abstract] 绑定的闭包函数, 执行函数参数是$parameters
return call_user_func_array($this->binds[$abstract], $parameters);
}
}

// 创建一个容器(后面称作超级工厂)
$container = new Container;

// 向该 超级工厂添加超能力模组的生产脚本
$container->bind('powerA', function($container) {
return new PowerA;
});

// 同上
$container->bind('powerB', function($container) {
return new PowerB;
});

// 向该 超级工厂添加超人的生产脚本
$container->bind('superman', function($container, $moduleName) {
return new Superman($container->make($moduleName));
});

echo "<pre>";

// 开始启动生产
$superman_1 = $container->make('superman', ['powerA']);
$superman_1->show(['a' => 1]);

$superman_2 = $container->make('superman', ['powerB']);
$superman_2->show(['b' => 1]);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: