Laravel 一些核心思想
一、依赖注入
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
通俗一点来讲不是由自己内部 new 对象或者实例,通过构造函数,或者方法传入的都属于 依赖注入。
这里我们使用开发中经常使用到的功能发邮件,在日常开发中,肯定会遇到有俩个服务商的短信吧。
下面使用代码来演示:
<?php
// 定义服务类
interface MailInterface
{
public function send();
}
// 服务实现类A
class MailA implements MailInterface
{
public function send()
{
echo 'MailA 发送';
}
}
// 服务实现类B
class MailB implements MailInterface
{
public function send()
{
echo 'MailB 发送';
}
}
// 服务提供者
class UserMail
{
protected $mail;
public function __construct()
{
$this->mail = new MailB();
}
public function sendTo()
{
echo '发送短信了';
$this->mail->send();
}
}
$mail = new UserMail();
$mail->sendTo();
这里插一个重要概念:服务提供者。
Laravel 服务提供者主要用来进行注册服务容器绑定(即注册接口及其实现类的绑定)。
再通俗一点,第一步是为了发邮件写了接口,接口的实现类写好了,那需要有地方来调用,服务提供者就是调用的类(这里的服务提供者是 UserMail),绑定和实例化应该是在这里完成。
这个时候,可以看到如果我们要更换服务商A的话,就需要去修改操作类。所以现在使用依赖注入来对操作类代码进行优化。
<?php
// 操作类
class UserMail
{
protected $mail;
public function __construct(MailInterface $mail)
{
$this->mail = $mail;
}
public function sendTo()
{
echo '发送短信了';
$this->mail->send();
}
}
$mail = new UserMail(new MailA());
$mail->sendTo();
这样一看代码,再加上上面的解释,是不是很清晰明了。
二、反射机制
反射是指在PHP运行状态中,扩展分析PHP程序,导出或提出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能称为反射API。
PHP 5 及以上版本中有 ReflectionClass ,报告了一个类的有关信息。
示例:
<?php
// 获取 UserMail 的 reflectionClass 对象
$reflector = new ReflectionClass(UserMail::class);
// 拿到 UserMail 的构造函数
$constructor = $reflector->getConstructor();
// 拿到构造函数的所有依赖
$dependencies = $constructor->getParameters();
// 创建新的 UserMail 对象
$newUserMail = $reflector->newInstance();
现在可以通过创建一个 make 方法,利用反射机制拿到 UserMail 的构造函数,进而得到构造函数的依赖参数,通过递归方式创建参数依赖,最后调用 newInstanceArgs() 方法创建实例。
<?php
// 操作类
class UserMail
{
protected $mail;
public function __construct(MailA $mail)
{
$this->mail = $mail;
}
public function sendTo()
{
echo '发送短信了';
$this->mail->send();
}
}
function make($concrete)
{
$reflector = new ReflectionClass($concrete);
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
return $reflector->newInstance();
} else {
$dependencies = $constructor->getParameters();
$instances = getDependencies($dependencies);
return $reflector->newInstanceArgs($instances);
}
}
function getDependencies($params)
{
$dependencies = [];
foreach ($params as $param) {
$dependencies[] = make($param->getClass()->name);
}
return $dependencies;
}
$mail = make('UserMail');
$mail->sendTo();
注意,这里没有使用到 依赖注入 ,没有完全达到解偶。所以这样肯定是不合格的。这就引出了另外一个概念 Ioc容器。
三、Ioc 容器
Ioc—Inversion of Control,即“控制反转”,就是具有依赖注入功能的容器,是可以创建对象的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
我们可以借助一个容器,提前把服务商对象、操作类都绑定到 Ioc 容器 中,如果后期需要修改服务商,也只需要修改 Ioc 容器 里的绑定记录就行了,就很好的解耦上面代码。具体实现思路
- Ioc 容器维护 binding 数组记录 bind 方法传入的键值对如:mail => MailA,userMail => UserMail
- 在 ioc -> make('UserMail') 的时候,通过反射拿到 UserMail 的构造函数,拿到构造函数的参数,发现参数是 UserMail 的构造函数参数 mail, 然后根据 mail 得到 MailA。
- 然后通过反射机制创建 $mail = new MailA();
- 最后通过 newInstanceArgs() 再创建 new UserMail($mail)
<?php
// 定义接口
interface MailInterface
{
public function send();
}
// 服务商A
class MailA implements MailInterface
{
public function send()
{
echo 'MailA 发送';
}
}
// 服务商B
class MailB implements MailInterface
{
public function send()
{
echo 'MailB 发送';
}
}
// 操作类
class UserMail
{
protected $mail;
public function __construct(MailInterface $mail)
{
$this->mail = $mail;
}
public function sendTo()
{
echo '发送短信了';
$this->mail->send();
}
}
class Ioc
{
public $binding = [];
public function bind($abstract, $concrete)
{
$this->binding[$abstract]['concrete'] = function ($ioc) use ($concrete) {
return $ioc->build($concrete);
};
}
public function make($abstract)
{
$concrete = $this->binding[$abstract]['concrete'];
return $concrete($this);
}
public function build($concrete)
{
$reflector = new ReflectionClass($concrete);
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
return $reflector->newInstance();
} else {
$dependencies = $constructor->getParameters();
$instances = $this->getDependencies($dependencies);
return $reflector->newInstanceArgs($instances);
}
}
protected function getDependencies($paramters)
{
$dependencies = [];
foreach ($paramters as $paramter) {
$dependencies[] = $this->make($paramter->getClass()->name);
}
return $dependencies;
}
}
$ioc = new Ioc();
// $ioc->bind('MailInterface', 'MailA'); ## 如果更换服务商只需要修改 bind 记录
$ioc->bind('MailInterface', 'MailB'); ## 如果更换服务商只需要修改 bind 记录
$ioc->bind('userMail', 'UserMail');
$mail = $ioc->make('userMail');
$mail->sendTo();
持续更新。。
不对之处,请多指教