前言
OOP(面向对象编程),早已深入人心,我们的程式也因此更具扩展性。但是,诸多的对象互相依赖,也让程式复杂性加深,DI(依赖注入)、IOC(容器化)的出现,便是解决此问题。接下来,我们通过实例,让大家更好的理解掌握。
DI 依赖注入
牧场饲养牛群,我们用程式表示
class Farm {
public $animal;
public function __construct() {
$this->animal = new Cattle();
}
}
interface Animal {
public function sound();
}
class Cattle implement Animal{
public function __construct() {}
public function sound() {
echo "Cattle makes a sound:mang~"
}
}
class Sheep implement Animal{
public function __construct() {}
public function sound() {
echo "Sheep makes a sound:mie~"
}
}
可见,Farm类与Cattle类产生依赖关系,并且,在Farm类内部实例化了Cattle类。显然违反了我们的"开闭原则"。
我们用依赖注入的方式,改写程式
class Farm {
public $animal;
public function __construct(Animal animal) {
$this->animal = new animal();
}
}
$Farm = new Farm(new Cattle());
Cattle类就和Farm类完成解耦,后续的依赖以注入的形式完成。
依赖注入(Dependency Injection):在程式运行过程中,动态将某种依赖关系注入到对象中
也可以简单理解为:以参数的形式,传递所需对象,到另外一个对象中。
Laravel中的依赖注入
class ExampleController {
public function Index(Request $request) {
var_dump($request->all());
}
}
Laravel框架内的Request传参,有应用到反射来实现DI
// 构造反射函数
$method = new ReflectionMethod('ExampleController', 'Index');
$args = [];
foreach($method->getParameters() as $parameter) {
if ($class = $parameter->getClass()) {
$args[] = new $class->name; //$request = new Request
}
}
$method->invokeArgs(new ExampleController, $args);
- ReflectionMethod,反射函数;ReflectionClass,反射类
- getParameter,循环参数组,如果是类,便实例化
- invokeArgs,使用数组给方法传递参数,并执行它
IOC 容器化
IOC (Inversion of Control):借助“第三方”,实现具有依赖关系的对象之间解耦
依赖关系即Farm类和Cattle类,那么第三方容器是什么,又如何通过它实现依赖关系的解耦呢,我们通过代码@来源来释义:
class Container
{
protected $binds; //注册实例
protected $instances; //实例
/**
* 向容器注册实例
*/
public function bind($abstract, $concrete)
{
if ($concrete instanceof Closure) {
// 绑定闭包,懒加载实例,此时未new实例
$this->binds[$abstract] = $concrete;
} else {
// 绑定实例
$this->instances[$abstract] = $concrete;
}
}
/**
* 从容器拿并new实例
*/
public function make($abstract, $parameters = [])
{
// 如果类已new,直接返回,无需懒加载
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
// 容器本身,插入到参数组首位
array_unshift($parameters, $this);
// 回调函数,并传入参数组
return call_user_func_array($this->binds[$abstract], $parameters);
}
}
$container = new $container; // 生成容器
$container->bind('Animal', function($container){
return new Cattle;
}); // 向容器注册Animal
$container->bind('Farm', function($container,$module){
return new Farm($container->make($module));
}); // 向容器注册Farm
$computer = $container->make('Farm','Animal'); // 实例化Farm,Animal作为参数,在Farm闭包内完成实例,进而完成依赖注入
- 通过懒加载节省性能开销,Farm类、Cattle类开始注册到容器类,只有正式使用,才会new实例
- 重设Farm类和Animal类关联,我们只需要改动Animal绑定下的Cattle即可
参考资料
Laravel中的服务容器 涂bug
浅谈IOC:说清楚IOC是什么 DebugLZQ
Laravel中应用反射实现依赖注入 PHP进阶架构师