内容简介:依赖倒置和控制反转是一种编程思想,而依赖注入就是通过服务容器实现这种面向接口或者是面向抽象编程的思想依赖倒置是一种软件设计思想,在传统软件中,上层代码依赖于下层代码,当下层代码有所改动时,上层代码也要相应进行改动,因此维护成本较高。而依赖倒置原则的思想是,当调用者需要被调用者的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例,但在这里,创建被调用者实例的工作不再由调用者来完成,而是将被调用者的创建移到调用者的外部,从而反转被调用者的创建,消除了调用者对被调用者创建的控制,因此称为控制反转。
依赖倒置和控制反转是一种编程思想,而依赖注入就是通过服务容器实现这种面向接口或者是面向抽象编程的思想
概念理解
依赖倒置原则
依赖倒置是一种软件设计思想,在传统软件中,上层代码依赖于下层代码,当下层代码有所改动时,上层代码也要相应进行改动,因此维护成本较高。而依赖倒置原则的思想是, 上层不应该依赖下层,应依赖接口 。意为上层代码定义接口,下层代码实现该接口,从而使得下层依赖于上层接口,降低耦合度,提高系统弹性
控制反转
当调用者需要被调用者的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例,但在这里,创建被调用者实例的工作不再由调用者来完成,而是将被调用者的创建移到调用者的外部,从而反转被调用者的创建,消除了调用者对被调用者创建的控制,因此称为控制反转。
要实现控制反转,通常的解决方案是将创建被调用者实例的工作交由 IoC 容器来完成,然后在调用者中注入被调用者(通过构造器/方法注入实现),这样我们就实现了调用者与被调用者的解耦,该过程被称为依赖注入。
依赖注入不是目的,它是一系列 工具 和手段,最终的目的是帮助我们开发出松散耦合(loose coupled)、可维护、可测试的代码和程序。这条原则的做法是大家熟知的 面向接口,或者说是面向抽象编程 。
通俗的说,在调用一个对象的方法,首先要实例化对象之后。 而所谓的注入,就是一种工厂模式的升华。由一个更高级的工厂(容器),来完成对象实例化,实现调用者与被调用者的解耦
解决什么问题
实现调用者与被调用者的解耦
[info] 所谓的上层代码依赖于 接口 ,就是业务逻辑的实现是跳过了具体对象的抽象行为。比如我们要对用户发消息,可以通过邮件发送,也可以通过短信发送。上层代码不用关注其用什么发送,只发送即可(适配器模式)
interface Mail
{
public function send();
}
class Email implements Mail
{
public function send()
{
echo '发送邮件' . PHP_EOL;
}
}
class SmsMail implements Mail
{
public function send()
{
echo '发送短信' . PHP_EOL;
}
}
// 注册容器
class Register
{
private $_mailObj;
// 构造函数里面已经约束了必须是实现了Mail接口的类的实例
public function __construct(Mail $mailObj)
{
$this->_mailObj = $mailObj;
}
public function doRegister()
{
// 一定会有send方法
$this->_mailObj->send();//发送信息
}
}
$emailObj = new Email();
$smsObj = new SmsMail();
$reg = new Register($emailObj);
$reg->doRegister();//使用email发送
$reg = new Register($smsObj);
$reg->doRegister($smsObj);//使用短信发送
使用构造函数注入的方法,使得它只依赖于发送短信的接口,只要实现其接口中的'send'方法,不管你什么方式发送都可以。上面通过构造函数注入对象的方式,就是最简单的依赖注入;当然"注入"不仅可以通过构造函数注入,也可以通过属性注入,上面你可以通过一个"setter"来动态为"mailObj"这个属性赋值。
通过 php 反射机制实现自动注入
真实的dependency injection container会提供更多的特性,如
- 自动绑定(Autowiring)或 自动解析(Automatic Resolution)
- 注释解析器(Annotations)
- 延迟注入(Lazy injection)
<?php
class C
{
public function doSomething()
{
echo __METHOD__ , '我是周伯通C|';
}
}
class B
{
private $c;
public function __construct(C $c)
{
$this->c = $c;
}
public function doSomething()
{
$this->c->doSomething();
echo __METHOD__ , '我是周伯通B|';
}
}
class A
{
private $b;
public function __construct(B $b)
{
$this->b = $b;
}
public function doSomething()
{
$this->b->doSomething();
echo __METHOD__ , '我是周伯通A|';;
}
}
class Container
{
private $s = [];
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);
}
if(!class_exists($className)){
throw new Exception("{$className} class is not exists");
}
/** @var ReflectionClass $reflector */
$reflector = new ReflectionClass($className);
// 检查类是否可实例化, 排除抽象类abstract和对象接口interface
if (!$reflector->isInstantiable()) {
throw new Exception("Can't instantiate this.");
}
/** @var ReflectionMethod $constructor 获取类的构造函数 */
$constructor = $reflector->getConstructor();
// 若无构造函数,直接实例化并返回, (注意! 此处退出递归1)
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)) {
// 是变量,有默认值则设置默认值 (注意,此处退出递归2)
$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.');
}
}
/*// example1
$container = new Container();
$container->b = 'B';
$container->a = function ($container){
return new A($container->b);
};
// 从容器中取得A
$model = $container->a;
// output: C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|
// 实现依赖自动注入
$model->doSomething();*/
// example2
$di = new Container();
$di->php7 = 'A'; // 自动注入classA
/** @var A $php7 */
$foo = $di->php7;
$foo->doSomething(); //C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|
参考:
以上所述就是小编给大家介绍的《【modernPHP专题(3)】依赖注入与服务容器》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 实现PHP的自动依赖注入容器 EasyDI容器
- PHP 依赖注入容器实现
- 浅析依赖倒转、控制反转、IoC 容器、依赖注入。
- 常用设计模式/容器/依赖注入/静态代理/请求对象
- 搞懂依赖注入, 用 PHP 手写简易 IOC 容器
- 软件工程入门-轻松理解依赖注入 (DI) 和 IoC 容器
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
编程的修炼(中英双语)
[荷]Edsger W. Dijkstra / 裘宗燕 / 电子工业出版社 / 2013-7 / 79.00元
本书是图灵奖获得者Edsger W. Dijkstra在编程领域里的经典著作中的经典。作者基于其敏锐的洞察力和长期的实际编程经验,对基本顺序程序的描述和开发中的许多关键问题做了独到的总结和开发。书中讨论了顺序程序的本质特征、程序描述和对程序行为(正确性)的推理,并通过一系列从简单到复杂的程序的思考和开发范例,阐释了基于严格的逻辑推理开发正确可靠程序的过程。 本书写于20世纪70年代中后期,但......一起来看看 《编程的修炼(中英双语)》 这本书的介绍吧!