依赖注入 - Dependence Inversion - DI
定义
依赖注入是指不是由内部生产(初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的。
解释
简单来说就是把依赖对象的实例化过程交给第三方(IOC容器)来实现。
控制 : 依赖对象实例化的控制。
正向 : 我们在对象中主动获取依赖对象。
反向 : IOC容器生成依赖对象,并且把依赖对象传递到资源对象中。资源对象获取依赖对象的方式反转了,由主动创建变成被动接受了。
IOC容器 : 一个类,它负责控制所有对象的生命周期和各个对象之间的关系。
结构中包含的角色
- IocContainer (IOC容器)
- AbstractResource (抽象资源))
- Resource (具体资源)
- AbstractResourceDependent (抽象资源依赖)
- ResourceDependent (资源依赖)
最小可表达代码
class ResourceDependent {}
class Resource
{
private $resourceDependent;
public function __construct(ResourceDependent $resourceDependent)
{
$this->resourceDependent = $resourceDependent;
}
}
// 依赖注入
$resource = new Resource(new ResourceDependent);
简化版Laravel服务容器代码
abstract class AbstractResourceDependent {}
class ResourceDependent extends AbstractResourceDependent {}
abstract class AbstractResource
{
private $resourceDependent;
public function __construct(AbstractResourceDependent $resourceDependent)
{
$this->resourceDependent = $resourceDependent;
}
public function debug()
{
return 'test';
}
}
class Resource extends AbstractResource {}
class IocContainer
{
// 绑定的数据
protected $bindings = [];
// 实例化的数据
protected $instances = [];
// 绑定数据
public function bind($abstract, $concrete = null)
{
// 包装成闭包
if (! $concrete instanceof Closure) {
$concrete = $this->getClosure($abstract, $concrete);
}
$this->bindings[$abstract] = compact('concrete');
}
// 包装成闭包的方法
protected function getClosure($abstract, $concrete)
{
return function ($container) use ($abstract, $concrete) {
if ($abstract == $concrete) {
return $container->build($concrete);
}
return $container->make($concrete);
};
}
// 解析数据
public function make($abstract)
{
// 已经有,直接返回
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
// 获取绑定的数据
$concrete = $this->getConcrete($abstract);
$object = $this->build($concrete);
return $object;
}
// 创建数据
public function build($concrete)
{
if ($concrete instanceof Closure) {
return $concrete($this);
}
// 暂时屏蔽反射生成的逻辑
// $reflector = new ReflectionClass($concrete);
return new $concrete;
}
// 获取绑定的数据
protected function getConcrete($abstract)
{
if (isset($this->bindings[$abstract])) {
return $this->bindings[$abstract]['concrete'];
}
return $abstract;
}
}
$app = new IocContainer;
$app->bind(AbstractResourceDependent::class, function ($app) {
return new ResourceDependent();
});
$app->bind(AbstractResource::class, function ($app) {
return new Resource($app->make(AbstractResourceDependent::class));
});
$resource = $app->make(AbstractResource::class);
var_dump($resource->debug());
实际应用场景
- Laravel的服务容器
- Spring的服务容器