参考: https://learnku.com/articles/14145/rely-on-inversion-control-inversion-ioc-container-dependency-injection
今天看到 浅析依赖倒转、控制反转、IoC 容器、依赖注入 这篇文件,让自己初步了解这些概念。记录一下这篇 重写仿照 laravel 的 IOC 容器和依赖注入 的代码
<?php
class Boos{
//领导依赖员工
private $staff;
//老板只需要告诉外部我需要什么样的人就好了,其它什么都不管,具体什么样的人交给外部处理。
//用构造方法方式实现依赖注入
public function __construct(Standard $staff){
$this->staff = $staff;
}
public function task(){
$this->staff->work();
}
}
//招聘所设定的标准
interface Standard{
public function work();
}
//员工需要依赖的标准
class StaffA implements Standard{
public function work(){
echo '雇员A有能力能够完成老板指定的工作';
}
}
class StaffB implements Standard{
public function work(){
echo '雇员B有能力能够完成老板指定的工作';
}
}
class Hr{
private $binds = [];
//接受不同员工的简历,并存起来 2、实例化后调用 bind 方法参数
// $contract = 'Standard', $concrete = 'StaffA'
public function bind($contract,$concrete){
//等于 $this->binds['Standard'] = 'StaffA';
$this->binds[$contract] = $concrete;
}
//询问老板选人的标准由哪些,并且从满足的简历中筛选人
private function methodBindParams($className){
// 5、 根据第4步 调用的 methodBindParams 方法 参数 $className = 'Boos',
// 这里进行实例化 reflect 类,并且传递参数 $className = 'Boos','__construct'
$reflect = new reflect($className,'__construct');
// 7、实例化完成后 调用 reflect 类的 bindParamsToMethod 方法并且返回
return $reflect->bindParamsToMethod();
}
//将选好的工作人员交给老板 3、调用 make 方法 参数 'Boos'
public function make($className){
// 4、调用 当前类里的 methodBindParams 方法,传递参数为 'Boos'
// 根据 第8步,最终 $methodBindParams = ['Boos' => [0 => ['staff', 'Standard']] ];
$methodBindParams = $this->methodBindParams($className);
// 9、 这里进行实例化 reflect 类,并且传递参数 $className = 'Boos','__construct'
$reflect = new reflect($className,'__construct');
// 10、调用 reflect 类的 make方法,
// $this->binds = ['Standard'=> 'StaffA'];
// $methodBindParams = ['Boos' => [0 => ['staff', 'Standard']] ];
// 根据11步,这里最终返回的是 Boos的实例化
return $reflect->make($this->binds,$methodBindParams);
}
}
class reflect{
private $className;
private $methodName;
// 6、根据第5步,实例化 reflect 类,也就是本类, 实例化后会先调用 __construct 构造方法
// 并且传递的参数为 $className = 'Boos',$methodName = '__construct'
public function __construct($className,$methodName){
$this->className = $className;
$this->methodName = $methodName;
}
//绑定参数到方法 8、根据第七步的调用 走此方法
public function bindParamsToMethod(){
$params = [];
// 8续、实例化一个 ReflectionMethod 类,$this->className,$this->methodName参数根据
// 第5步 实例化时传递的 $className = 'Boos',$methodName = '__construct'
// 实际就时获取的 Boos类的 __construct 方法的详情
$method = new ReflectionMethod($this->className,$this->methodName);
// 8续、根据上面这行代码 拿到的 __construct 方法详情,然后 getParameters 方法获取
// __construct 方法的参数,按顺序进行获取,返回的是个数组 所以这里进行foreach循环
foreach ($method->getParameters() as $param) {
// 8续、 $param->name 是 Boos 类的__construct的参数名称,Boos的
// __construct(Standard $staff) 方法只有一个参数,这里循环只会循环一次
// $param->getClass()->name 获取的是变量的对象声明,类型声明无法获取。
// 最终 $param->name 为 'staff' , $param->getClass()->name 为 'Standard'
$params[] = [$param->name,$param->getClass()->name];
}
// 8续、然后返回,$this->className 为 'Boos',
// $params 为 $params = [ 0 => ['staff', 'Standard'] ];
// 最终为 ['Boos' => [0 => ['staff', 'Standard']] ];
return [$this->className => $params];
}
// 11、根据第10步调用的本方法 make 并且传递的参数
// $this->binds = ['Standard'=> 'StaffA'];
// $methodBindParams = ['Boos' => [0 => ['staff', 'Standard']] ];
public function make($bind,$methodBindParams){
$args = [];
foreach ($methodBindParams as $className => $params) {
foreach ($params as $param) {
list($paramName,$paramType) = $param;
// 11续、 根据传递的参数循环,
// new $bind[$paramType]()等于 new $bind['Standard']()
// new $bind['Standard']() 等于 new StaffA();
// 这里 $paramName 就是实例对象
$paramName = new $bind[$paramType]();
// 然后添加到 $args 数组中
array_push($args, $paramName);
}
}
// 11续、根据第9步,实例化本类的时候传递的参数 $className = 'Boos',
// 所以这里获取的是 Boos 类详情
$reflectionClass = new ReflectionClass($this->className);
// 11续、这里进行 Boos 类实例化,并且把 $args 参数传递到 Boos类的构造函数
// 根据上面的循环实例化,$args数组中的每个元素都是 一个实现了 Standard 接口的类
// 根据 Boos 类的 __construct(Standard $staff) 类型声明 传递是没问题的
// 最终返回的是 Boos类实例化
return $reflectionClass->newInstanceArgs($args);
}
}
// 1、实例化 Hr 类
$hr = new Hr();
//老板如果需要换工作人员,只需要绑定其它的工作人员即可。
//2、实例化后调用 bind 方法
$staff = $hr->bind('Standard','StaffA');
// 3、调用 make 方法 返回的是Boos类实例化,并且把 StaffA 传递进了Boos类的构造函数
// 根据第11步进行传递的
$boos = $hr->make('Boos');
// 12、终篇 然后这里调用 task()
// $this->staff->work(); $this->staff也就是 StaffA 的实例化,
// 在第11步进行传递进Boos类的构造函数
$boos->task();