背景

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

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

一、概念

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

2、IoC - Inversion of Control 控制反转

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

3、DI - Dependency Injection 依赖注入

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

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

二、依赖注入的原理

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

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

而依赖注入方式如下:

解析:

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

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

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

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

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

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

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

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

<?php
class IoC
{protected static $registry = [];public static function bind($name, Callable $resolver) //传入类名和类对象实例{static::$registry[$name] = $resolver;}public static function make($name) //静态工厂方法{if (isset(static::$registry[$name])) {$resolver = static::$registry[$name];return $resolver(); //实例化}throw new Exception('Alias does not exist in the IoC registry.');}
}

总而言之,三种方式传递的都是实例化对象,只是不同之处在于传递的位置分别为构造方法、set属性、静态工厂方法而已。

四、依赖注入容器(Ioc容器)

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

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

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

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

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

<?php //依赖关系:Company->Department->Group
class Group
{public function doSomething(){echo __CLASS__.":".'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容器的内部实现:

<?php
class Container
{private $s = array();public function __set($k, $c){$this->s[$k] = $c;}public function __get($k){return $this->build($this->s[$k]);}/*** 自动绑定(Autowiring)自动解析(Automatic Resolution)** @param string $className* @return object* @throws Exception*/public function build($className){// 如果是匿名函数(Anonymous functions),也叫闭包函数(closures)if ($className instanceof Closure) {// 执行闭包函数,并将结果return $className($this);}/*通过反射获取类的内部结构,实例化类*/$reflector = new ReflectionClass($className);// 检查类是否可实例化, 排除抽象类abstract和对象接口interfaceif (!$reflector->isInstantiable()) {throw new Exception("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的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。Spring容器负责将这些联系在一起。也就是说,Spring的IOC负责管理各种对象的创建、清除以及它们之间的联系。

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

  1. 依赖注入神器:Dagger2详解系列

    依赖注入神器:Dagger2详解系列 序言 Dagger2是啥 Dagger2是啥,Google告诉我们: Dagger is a fully static, compile-time depende ...

  2. Spring —— IoC 容器详解

    引言 本篇博客总结自官网的<The IoC Container>,其中会结合王富强老师的<Spring揭秘>融入自己的语言和理解,争取通过这一篇文章彻底扫除spring IOC ...

  3. Spring——依赖注入(DI)详解

    声明:本博客仅仅是一个初学者的学习记录.心得总结,其中肯定有许多错误,不具有参考价值,欢迎大佬指正,谢谢!想和我交流.一起学习.一起进步的朋友可以加我微信Liu__66666666 这是简单学习一遍之 ...

  4. php 定位_PHP进阶学习之Geo的地图定位算法详解

    本文实例讲述了PHP进阶学习之Geo的地图定位算法.分享给大家供大家参考,具体如下: 前言 日常开发中我们经常需要查找某个物体的定位,或者查找附近的范围等,我们自然而然会想到的方法就是利用各种提供服务 ...

  5. Spring : 依赖注入(IoC)控制反转

    1.美图 2.概述 依赖注入(Dependecy Injection)和控制反转(Inversion of Control)是同一个概念,具体的讲:当某个角色需要另外一个角色协助的时候,在传统的程序设 ...

  6. 【Spring】Spring 自动注入(autowire)详解

    1.概述 转载:添加链接描述 2. 手动注入的不足 [Spring]Spring 依赖注入之手动注入 上篇文章中介绍了依赖注入中的手动注入,所谓手动注入是指在xml中采用硬编码的方式来配置注入的对象, ...

  7. php laravel入口文件,Laravel学习教程之从入口到输出过程详解

    php 的 Laravel学习教程之从入口到输出过程详解 本文主要给大家介绍了关于Laravel从入口到输出过程的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. I. 预备 ...

  8. Spring第8篇:自动注入(autowire)详解

    本文内容 手动注入的不足 Class.isAssignableFrom方法介绍 3种自动注入方式详解及案例 按名称自动注入 按类型自动注入 按构造器进行自动注入 按类型自动注入某种类型的所有bean给 ...

  9. Spring系列之自动注入(autowire)详解

    本文内容 手动注入的不足 Class.isAssignableFrom方法介绍 3种自动注入方式详解及案例 按名称自动注入 按类型自动注入 按构造器进行自动注入 按类型自动注入某种类型的所有bean给 ...

最新文章

  1. 牛!月入2w,95后送外卖的程序员,送餐途中改bug
  2. 用GAN来做图像生成,这是最好的方法
  3. 手摸手教你使用vue-cli脚手架-详细步骤图文解析[vue入门]
  4. 导出Excel java
  5. 2个字段并在一次插入一个字段里面_elasticsearch外用与内观(二)-当插入文档时,elasticsearch都在做什么...
  6. 一起玩转CoordinatorLayout
  7. Delphi 7自带的TeeChart组件
  8. 未来云原生世界的“领头羊”:容器批量计算项目Volcano 1.0版本发布
  9. 【java笔记】异常处理
  10. MQ(队列消息的入门)
  11. 给 IDEA 换个酷炫的主题吧,这个有点哇塞啊!
  12. USB接口类型及引脚定义-usb1.0,usb2.0,usb3.0,Type-c
  13. linux u盘保护,如何解除U盘写保护状态?
  14. Day434.订单库存服务分布式事务的最终解决 -谷粒商城
  15. 亚马逊云科技荣获2021中国公有云平台用户满意度第一
  16. CANopen协议解读
  17. 语音控制Office,这个功能一定要体验
  18. Java txt文件 转 utf-8 格式
  19. 不只是同构应用(isomorphic 工程化你所忽略的细节)
  20. 实用工具(锐捷睿易篇)

热门文章

  1. 为什么先交钱后用电_适合痘痘痘印的好的护肤品,为什么要先清洁后祛痘!_新闻中心...
  2. 记录爬取信用中国,里面的行政许可内容,行政处罚,守信激励的内容,并以excel形式显示
  3. pandas.get_dummies
  4. 我们又来送书啦!这一次,用做业务的思维讲推荐系统 | 文末福利
  5. WWW 2021|基于图神经网络的分级相关性匹配
  6. 效率飞起!BML CodeLab发布重磅更新
  7. 2021年,投身自然语言处理是否明智?
  8. 万物皆可文本时代来临?如何搞定NLP最强模型GPT
  9. 10万元奖金语音识别赛进行中!CTC 模型 Baseline 助你轻松上分
  10. ZOJ - 1450 Minimal Circle HDU - 3007 Buried memory 最小圆覆盖模板 【随机函数】【增量法】