依赖注入
我想 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) {
}
}
}