本文实例讲述了PHP依赖注入与Ioc容器。分享给大家供大家参考,具体如下:

背景

在很多编程语言(例如java)开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,一旦有修改,牵扯的类会很多。

最早在java的spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。目前许多主流PHP框架也使用了依赖注入容器,如ThinkPHP、Laravel等。

一、概念

1、容器:字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,现在我们讨论的是这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调(闭包),通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦”、“依赖注入”。

2、IoC - Inversion of Control 控制反转 

控制反转是从容器的角度在描述,即:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。 

3、DI - Dependency Injection 依赖注入 

依赖注入是从应用程序的角度在描述,可以把依赖注入,即:应用程序依赖容器创建并注入它所需要的外部资源。

备注:依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,从某个方面讲,就是它们描述的角度不同。 

二、依赖注入的原理

一般情况下,当存在类与类之间的依赖关系的时候,我们都是通过直接实例化的方式进行调用。一旦出现多层依赖,这种方式的耦合程度就很高,在需要修改其中一个类的时候,会牵扯很多依赖它的类的修改,因此对代码的改动会比较大。

下面简单举一个A->B->C三层依赖的关系解释怎么运用依赖注入来解耦,提高开发效率。

PHP进阶学习之依赖注入与Ioc容器详解

而依赖注入方式如下:

PHP进阶学习之依赖注入与Ioc容器详解

解析:

常规写法里面,一旦C类需要作出改变,或者B类的调用需要改变成D类的时候,还需要考虑到依赖自己的B类,即还需要对B类作出修改。

依赖注入的思想就是即用即实例,反转类与类之间的控制关系,实现由调用类A类控制后续的依赖关系,这样可以让B类随意的更改所需依赖和实例化的类(C类或D类),达到解耦的目的。

PHP进阶学习之依赖注入与Ioc容器详解

三、常用的依赖注入方式:

1、构造方法注入;2、set属性注入;3、静态工厂方法注入;

上述的例子使用的就是构造方法注入的方式,将对象作为参数传递到构造方法中;同样的set属性注入也是相类似的方法,不同的仅仅是在set一个类的成员的属性时传递这个对象参数,在此就不一一举例了。

除此之外,还有静态工厂方法注入的方式,这种方法与静态工厂方法类似。

我们知道静态工厂方法就是通过一个类来管理需要实例化的多个相似的类,该类会定义一个方法用于获取需要实例化的对象,而具体要实例化哪个对象就依赖于传递进来的对象名参数了。

对于静态工厂方式的注入,与一般的静态工厂方法不同之处在于这个传进来的参数是一个已经实例化过的对象。

<"font-size: medium">四、依赖注入容器(Ioc容器)

大多数时侯,在使用依赖注入方式解耦组件时,并不需要用到容器。
当一段程序需要实例化的类太多或者依赖太多的时候,重复依赖注入的代码是比较繁琐的事情,例如以下情况:

PHP进阶学习之依赖注入与Ioc容器详解

当产生以上关系的时候,依赖注入的代码会比较混乱,而且存在重复,更有可能在调用一个一般方法时new一个不需要的类,产生冗余。

此时需要使用容器,使用依赖注入容器后的思路是应用程序需要到A类,就从容器内取得A类。具体是容器创建C类,再创建B类并把C注入,再创建A类,并把B类注入,应用程序调用A类方法, A类调用B类方法,接着做些其它工作.总之容器负责实例化,注入依赖,处理依赖关系等工作。

PHP进阶学习之依赖注入与Ioc容器详解

PHP进阶学习之依赖注入与Ioc容器详解

对于实际开发中复杂多变的代码环境,我们并不能完全知道现在的类在未来会扩展成什么情况,因此我们需要在有新的依赖类加入的时候,通过容器去实现实例化该类的方法。因此,在实例化未知类的时候,最能探索一个类的内部结构和实例化的方法就是利用反射,由此可知,反射是容器管理各个依赖类的核心。我们可以通过实例来了解容器的内部实现:

三个存在依赖关系的类:文件testClass.php

<":".'hello', '|';
  }
}
class Department
{
  private $group;
  public function __construct(Group $group)
  {
    $this->group = $group;
  }
  public function doSomething()
  {
    $this->group->doSomething();
    echo __CLASS__.":".'hello', '|';
  }
}
class Company
{
  private $department;
  public function __construct(Department $department)
  {
    $this->department = $department;
  }
  public function doSomething()
  {
    $this->department->doSomething();
    echo __CLASS__.":".'hello', '|';
  }
}

Ioc容器的内部实现:

<"Can't instantiate this.");
    }
    /** @var ReflectionMethod $constructor 获取类的构造函数 */
    $constructor = $reflector->getConstructor();
    // 若无构造函数,直接实例化并返回
    if (is_null($constructor)) {
      return new $className;
    }
    // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表
    $parameters = $constructor->getParameters();
    // 递归解析构造函数的参数
    $dependencies = $this->getDependencies($parameters);
    // 创建一个类的新实例,给出的参数将传递到类的构造函数。
    return $reflector->newInstanceArgs($dependencies);
  }
  /**
   * @param array $parameters
   * @return array
   * @throws Exception
   */
  public function getDependencies($parameters)
  {
    $dependencies = [];
    /** @var ReflectionParameter $parameter */
    foreach ($parameters as $parameter) {
      /** @var ReflectionClass $dependency */
      $dependency = $parameter->getClass();
      if (is_null($dependency)) {
        // 是变量,有默认值则设置默认值
        $dependencies[] = $this->resolveNonClass($parameter);
      } else {
        // 是一个类,递归解析
        $dependencies[] = $this->build($dependency->name);
      }
    }
    return $dependencies;
  }
  /**
   * @param ReflectionParameter $parameter
   * @return mixed
   * @throws Exception
   */
  public function resolveNonClass($parameter)
  {
    // 有默认值则返回默认值
    if ($parameter->isDefaultValueAvailable()) {
      return $parameter->getDefaultValue();
    }
    throw new Exception('I have no idea what to do here.');
  }
}
require_once "./testclass.php"; //开始测试,先测试已知依赖关系的情况
$c = new Container();
$c->department = 'Department';
$c->company = function ($c) {
  return new Company($c->department);
};
// 从容器中取得company
$company = $c->company;
$company->doSomething(); //输出: Group:hello|Department:hello|Company:hello|
// 测试未知依赖关系,直接使用的方法
$di = new Container();
$di->company = 'Company';
$company = $di->company;
$company->doSomething();//输出: Group:hello|Department:hello|Company:hello|

我们可以通过一张图解释Ioc容器的内部逻辑:

PHP进阶学习之依赖注入与Ioc容器详解

五、总结

IOC的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。Spring容器负责将这些联系在一起。也就是说,Spring的IOC负责管理各种对象的创建、清除以及它们之间的联系。 

更多关于PHP相关内容感兴趣的读者可查看本站专题:《php面向对象程序设计入门教程》、《PHP数组(Array)操作技巧大全》、《PHP基本语法入门教程》、《PHP运算与运算符用法总结》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》

希望本文所述对大家PHP程序设计有所帮助。

标签:
PHP,依赖注入,Ioc容器

免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
狼山资源网 Copyright www.pvsay.com

评论“PHP进阶学习之依赖注入与Ioc容器详解”

暂无“PHP进阶学习之依赖注入与Ioc容器详解”评论...

稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!

昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。

这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。

而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?