1. 控制反转
1.1. Ioc(容器)是什么
Ioc - Inversion of Control 即 “控制反转” ,它不是什么技术,而是一种设计思想。Ioc意味着将你设计好额对象放再一个叫容器控制里面(也就是你所封装好的对象方法常用的放在一个容器里面,容器里面可以做好关联,以便后面方便使用)而不是传统的在你的对象内部直接使用控制,如果理解好Ioc呢,理解好Ioc的关键是要明确 “谁控制谁,控制什么,为何是反转(有反转就有正转)”,哪些方面反转,那么我们来深入了解以下。
谁控制谁,控制什么
传统程序设计模式,我们是直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象(也就是传统在操作对象的时候,逻辑里面需要用到什么,就去new什么,如果一个对象里面还需要用到其他对象,而要在当前对象里面在new其他对象来调用),而Ioc是由专门一个容器来创建这些对象,即由容器来控制对象创建。谁控制谁,当然是Ioc控制了对象,控制什么?拿就是主要控制了外部资源获取。
为何是反转,哪些反面反转了,有反转就是有正转,产痛应用程序时由我们自己在对象中主动控制去直接获取依赖对象,也就是正转:而反转则时由容器来帮忙创建及注入依赖对象,为何是反转?因为容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转
Ioc能做什么?
IOc不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出耦合、更优良的程序,传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试,有了Ioc容器后,把创建和查找依赖对象的控制权交给了容器(举个栗子:有个订单操作类,在订单操作类里面需要用到订单通知类、订单日志类等,而在订单操作类里面还需要引入通知以及日志两个类,这样很难后期维护,有了容器,可以将订单的通知类以及订单的日志类全部放在一个容器里面把他们关联起来,需要用到的时候自己从容器里面拿出来即可),由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样方便测试,利于功能服用,更重要的是是的程序的整个体系结构变的非常灵活
依赖注入:
DI - Dependency Injection 即 “依赖注入”:组件之间依赖关系,由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中(上面的订单日志类以及订单的通知类,如果订单通知类里面需要用到订单的日志类,我们可以把通知类注入到订单日志类里面也就是关联一起),依赖注入的目的并非为软件系统带来更多的功能,而是为了替身组件重要的频率,作为系统搭建一个灵活、可扩张性的平台,通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就指定目标需要的资源,完成自身的业务逻辑,而不是关心具体的资源来自何处,由谁实现
理解DI的关键是“谁依赖谁,为什么需要注入,注入了什么”,那我们来深入分析一下
- 谁依赖谁:当时是应用程序依赖于Ioc容器(也就是说,每次需要用到的操作,必须依赖从容器里面拿出来)
- 为什么需要依赖:应用程序需要Ioc容器来提供对象需要的外部资源
- 谁注入谁:很明显是Ioc容器注入了应用程序的某个对象,应用程序依赖的对象
注入了什么:就是注入某个对象所需要的外部资源:
“依赖注入”:明确描述了“被注入对象依赖ioc容器配置依赖对象”
“依赖注入”:其实就是把相关的操作类,设计到的逻辑,全部注入到一起,注入到一个容器里面方便使用
传统模式,控制正转,栗子:
<?php
// 传统模式,控制正转
class A1
{
// 用来存储对象
protected $b1;
public function __construct()
{
$this->b1 = new B1();
}
public function getB()
{
return $this->b1;
}
}
class B1
{
public function __construct()
{
}
public function method()
{
echo '我是B类方法';
}
}
$a = (new A1())->getB()->method();
var_dump($a);
上面的代码:很容易理解为就是,A依赖了B
也就是说,如果今后开发中,要对B类修改,一旦方法名或则函数参数都变动了,甚至整个类发生了变化,我们也要对A类相应的修改,否则A类就会对B类丧失了功能。也就是“牵一发动全身”。、
解决这个问题:需要用到控制反转
控制反转例子:
例子如下:
<?php
class A
{
// 接受容器的对象
protected $ioc;
public function __construct(Ioc $ioc)
{
// 将容器存储起来,方便使用容器里面的其他方法
$this->ioc = $ioc;
}
// 例如需要在A类里面使用到B类的方法
// 这一步就是注入相关的B类
public function getB()
{
return $this->ioc->make('B');
}
}
class Ioc
{
// 存储注入的对象
protected $instance = [];
public function __construct()
{
// 将B类注入到Ioc容器中并存储到注入对象数组中
// 传入$this 是方便与A类里面可以使用到B类里面的方法逻辑,依赖注入关系
// 其实这一步的类名不应该是写死的,需要另外一中工厂模式来动态修改
$this->instance['B'] = new B($this);
$this->instance['A'] = new A($this);
}
// 要使用的时候,必须先创建,创建你想用到的对象
// 这里面其实还需要检测是否需要依赖注入关系
public function make($class_name)
{
return $this->instance[$class_name];
}
}
class B
{
protected $ioc;
public function __construct(Ioc $ioc)
{
// 将Ioc容器存储起来,方便可以使用到容器里面的其他方法
$this->ioc = $ioc;
}
// 在B类里面使用到A类的方法
public function getA()
{
return $this->ioc->make('A');
}
}
// 第一步:先创建容器
// 容器里面已经注入了两个类,一个是B类一个是A类
$obj = new Ioc();
// 想用A类
$a = $obj->make('A');
// 使用A类里面调用B类的方法
// 例如需求上在A类需要用到B类的操作逻辑,所以在A类里面也注入了B类对象,这样就方便使用
var_dump($a->getB());