懵懂时,我一直不理解为什么要有IOC容器这东西,composer不是已经可以实现自动加载了吗?
一个开发框架有很多的类库、实例等等需要管理和调用,IOC容器可以让我们更好的管理我们的类和对象,当前主流的框架已经离不开它了
别走客官,这是学习武林秘籍第一步,想要习得上层武功,必经之路呀,否则你只能去隔壁练葵花宝典了!
学习它之前,我们需要弄清2个概念
- 依赖注入
- 控制反转
- 最后我们将实现一个简单的自动依赖注入的IOC容器
依赖注入
我想 new 一个实例,但是这个实例中还需要 new 另外一个实例,也就是说两个实例对应的类是包含(组合)关系。
别逼逼,上代码
class A {
private $objB;
public function __construct (B $obj) {
$this->objB = $obj;
}
}
class B {}
//想拿A就得new B,嘿嘿
$instance = new A(new B());
依赖注入表示的是两个类的实例之间的共存关系
控制反转
为什么要有控制反转呢,上面的依赖注入是我们手动完成的,当某个对象需要另外一个对象的时候,我们就直接手动给它 new 一个出来,有没有更加高效的方式呢?IOC 控制反转应运而生,因为不需要你手动new就能自动产生你需要对象,怎么样,很神奇吧!
说到控制反转,我们不得不提的编程语言的反射机制,试想一下 new 一个类的时候,我只要知道类名就能实例化,是不是意味着只要你给我名字,我就控制了你这个类,那么我们称之为反射。
- 告诉我你需要的类的名字叫A,我通过一个叫ReflectionClass 来获得他的反射对象$reflector
$reflector = new ReflectionClass('App\A');
- isInstantiable() 检查类是否可实例化,newInstance()获得一个实例
if ($reflector->isInstantiable()) {
return $refector->newInstance();
}
- 怎么样,能拿到想要的实例吧,试想一下,如果我们把这种好用的特性结合到依赖注入会怎么样呢,是不是意味着我们就不用手动 new 实例了,直接拿到类名,自动 new 呗,还要啥自行车。
一个类实例化肯定会调用构造方法 __construct()
我们抓住这一点
$constructor = $reflector->getConstructor();
$dependencies = $constructor->getParameters();//获得构造方法的参数
你依赖的类这下全部给我在$dependencies中了吧,那么我一个个new 不就来了么
$dependenciesInstances = [];
foreach($dependencies as $dependency) {
$reflector = new ReflectionClass($dependency);
$dependenciesInstances[] = $refector->newInstance();
}
至此,我们是不是可以通过构造方法自动new依赖的实例了,效率大大的提高。
实现一个很简单的自动依赖注入工厂类
下面我们来写一个简单的自动依赖注入的工厂来解决我们开头说重复 new 的问题
class IocFactory {
/**
* make依赖实例
* @param $class
* @return object
* @throws ReflectionException
*/
public static function make($class) {
$reflector = new ReflectionClass($class);
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
if ($reflector->isInstantiable()) {
return $reflector->newInstance();
}
}
$dependenciesArgs = [];
$dependencies = $constructor->getParameters();//获得构造方法的参数
foreach($dependencies as $dependency ) {
//实例化依赖
$dependenciesArgs[] = self::make($dependency->getName());
}
return $reflector->newInstanceArgs($dependenciesArgs);
}
}
下面我们声明三个类,A类包含B类包含C类 也就是 A=>B=>C
class A {
private $b;
public function __construct(B $b)
{
$this->b = $b;
}
}
class B {
private $c;
public function __construct(C $c)
{
$this->c = $c;
}
}
class C {
}
现在可以实例化A,就可以自动帮我们把B和C也实例化
$obj = IocFactory::make(A::class);
var_dump($obj);
结果
object(A)#6 (1) {
["b":"A":private]=>
object(B)#7 (1) {
["c":"B":private]=>
object(C)#8 (0) {
}
}
}