专注于分布式,性能优化,代码之美

我对依赖注入,控制反转的理解

最近看依赖注入,控制反转

 IoC——Inversion of Control  控制反转
 DI——Dependency Injection   依赖注入

问题一:首先什么是依赖注入,控制反转

问题二:谁依赖谁,反转谁

问题三:参与者都有谁?

问题四:依赖:谁依赖于谁?为什么需要依赖?

问题五:注入:谁注入于谁?到底注入什么?

问题六:控制反转:谁控制谁?控制什么?为何叫反转(有反转就应该有正转了)?

问题七:依赖注入和控制反转是同一概念吗?

下面就来简要的回答一下上述问题,把这些问题搞明白了,IoC/DI也就明白了。

  1. 参与者都有谁:一般有三方参与者,一个是某个对象;一个是IoC/DI的容器;另一个是某个对象的外部资源。又要名词解释一下,某个对象指的就是任意的、普通的对象; IoC/DI的容器简单点说就是指用来实现IoC/DI功能的一个框架程序;对象的外部资源指的就是对象需要的,但是是从对象外部获取的,都统称资源,比如:对象需要的其它对象、或者是对象需要的文件资源等等。
  2. 谁依赖于谁:当然是某个对象依赖于IoC/DI的容器
  3. 为什么需要依赖:对象需要IoC/DI的容器来提供对象需要的外部资源
  4. 谁注入于谁:很明显是IoC/DI的容器 注入 某个对象
  5. 到底注入什么:就是注入某个对象所需要的外部资源
  6. 谁控制谁:当然是IoC/DI的容器来控制对象了
  7. 控制什么:主要是控制对象实例的创建
  8. 为何叫反转:反转是相对于正向而言的,那么什么算是正向的呢?考虑一下常规情况下的应用程序,如果要在A里面使用C,你会怎么做呢?当然是直接去创建C的对象,也就是说,是在A类中主动去获取所需要的外部资源C,这种情况被称为正向的。那么什么是反向呢?就是A类不再主动去获取C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的注入到A类中。

任何新事物的出现都是为了解决问题:

业务背景:

1.我们先来介绍控制反转,依赖注入,这两个概念我们可以认为他们表达的同一种意思,举个通俗的例子,我们用户登录需要提供记录日志的功能,可以选择使用文件或者数据库。下面我们用代码来演示。

 

/ 定义写日志的接口规范
interface log
{
public function write();
}

// 文件记录日志
class FileLog implements Log
{
public function write(){
echo 'file log write...';
}
}

// 数据库记录日志
class DatabaseLog implements Log
{
public function write(){
echo 'database log write...';
}
}

// 程序操作类
class User
{
protected $fileLog;

public function __construct()
{
$this->fileLog = new FileLog();
}

public function login()
{
// 登录成功,记录登录日志
echo 'login success...';
$this->fileLog->write();
}

}

user=newUser();user->login();

 

2.好了,上面的写法基本可以实现记录日志的功能,

但是有一个问题,假设现在想用数据库记录日志的话,我们就得修改User类,这份代码没达到解耦合,也不符合编程开放封闭原则,那如何修改呢?我们可以把日志处理类通过构造函数方式传递进去。下面我们试着修改User类的代码。

class User
{
protected $log;

public function __construct(Log $log)
{
this>log=log;
}

public function login()
{
// 登录成功,记录登录日志
echo 'login success...';
$this->log->write();
}

}

$user = new User(new DatabaseLog());
$user->login();

 

3.这样想用任何方式记录操作日志都不需要去修改过User类了,只需要通过构造函数参数传递就可以实现,其实这就是“控制反转”。不需要自己内容修改,改成由外部外部传递。这种由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)

那什么是依赖注入呢?,其实上面的例子也算是依赖注入,不是由自己内部new对象或者实例,通过构造函数,或者方法传入的都属于依赖注入(DI)

依赖注入

初学laravel的同学应该都比较好奇?很多对象实例通过方法参数定义就能传递进来,调用的时候不需要我们自己去手动传入。下面举一个laravel中实际的例子 Request对象 会都被自动的注入到函数里。是不是比较好奇呢?laravel是如何做到呢?

 

// routes/web.php
Route::get('/post/store', 'PostController@store');

// App\Http\Controllers
class PostController extends Controller {

public function store(Illuminate\Http\Request $request)
{
this>validate(request, [
'category_id' => 'required',
'title' => 'required|max:255|min:4',
'body' => 'required|min:6',
]);
}

}

反射理解

我们现在已经明白了依赖注入的概念。那laravel那种用法怎么实现呢?可能有些同学已经想到了这里面肯定会用到反射机制去创建动态Post,然后去调用store方法。

反射的概念其实可以理解成根据类名返回该类的任何信息,比如该类有什么方法,参数,变量等等。我们先来学习下反射要用到的api。拿User举例
 

// 获取User的reflectionClass对象
$class = new reflectionClass(User::class);

// 拿到User的构造函数
constructor=class->getConstructor();

// 拿到User的构造函数的所有依赖参数
dependencies=constructor->getParameters();

// 创建user对象
user=reflector->newInstance();

// 创建user对象,需要传递参数的
user=reflector->newInstanceArgs($dependencies = []);

 

这时候我们可以创建一个make方法,传入User,利用反射机制拿到User的构造函数,进而得到构造函数的参数对象。用递归的方式创建参数依赖。最后调用newInstanceArgs方法生成User实例。 可能有些同学还不是很理解。下面我们用代码去简单模拟下

 

function make(concrete){
// 或者User的反射类
reflector=newReflectionClass(concrete);
// User构造函数
constructor=reflector->getConstructor();
// User构造函数参数
dependencies=constructor->getParameters();
// 最后生成User
return reflector>newInstanceArgs(dependencies);
}

user=make(User);user->login();

具体代码实现

 

// 注意我们这里需要修改一下User的构造函数,如果不去修改。反射是不能动态创建接口的,那如果非要用接口该怎么处理呢?下一节我们讲Ioc容器的时候会去解决。

class User
{
protected $log;

public function __construct(FileLog $log)
{
this>log=log;
}

public function login()
{
// 登录成功,记录登录日志
echo 'login success...';
$this->log->write();
}

}

function make($concrete){

reflector=newReflectionClass(concrete);
constructor=reflector->getConstructor();
// 为什么这样写的? 主要是递归。比如创建FileLog不需要传入参数。
if(is_null($constructor)) {
return $reflector->newInstance();
}else {
// 构造函数依赖的参数
dependencies=constructor->getParameters();
// 根据参数返回实例,如FileLog
instances=this->getDependencies(dependencies);returnreflector->newInstanceArgs($instances);
}

}

function getDependencies($paramters) {
dependencies=[];foreach(paramters as paramter)$dependencies[]=make($paramter>getClass()>name);returndependencies;
}

user=make(User);user->login();

 

最后贴上原文章链接:https://xueyuanjun.com/post/9782.html

以及代码下载地址:https://github.com/cxp1539/laravel-core-learn/blob/master/code/Class1.php

posted on   xiaohouye  阅读(478)  评论(0编辑  收藏  举报

编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
今日之劳累是为了铸造明日之辉煌,不管年龄多少,都无法阻挡我对软件艺术的追求!
点击右上角即可分享
微信分享提示