整个 Laravel 框架的基石是一个功能强大的 IoC 容器(控制反转容器),如果你想真正从底层理解 Laravel 框架,就必须好好掌握它。不过,也不要被这个名头吓住,要知道 IoC 容器只不过是一种用于方便我们实现「依赖注入」这种软件设计模式的工具。而且要实现依赖注入并不一定非要通过 IoC 容器,只是使用 IoC 容器会更容易一点儿。

首先,来看看我们为何要使用依赖注入,或者说它能为我们的软件开发带来什么好处。考虑下列代码中的类和方法:

class UserController extends BaseController
{public function getIndex(){$users = User::all();return View::make('users.index', compact('users'));}
} 

这段代码看起来很简洁,但是不与数据库打交道的话,我们将无法测试这段代码。也就是说,Eloquent ORM 和该控制器有着紧耦合关系。如果不使用 Eloquent ORM,不连接到实际数据库,我们就没办法运行或者测试这段代码。同时,这段代码也违背了「关注点分离」这个软件设计原则。简单来讲:控制器知道的太多了。控制器不需要去了解数据是从哪儿来的,只要知道如何访问就行。控制器也不需要知道数据在 MySQL 中是否有效,只需要知道它目前是可用的。

关注点分离:每一个类都应该是单一职责的,并且这个职责应该完全被这个类封装。

所以,如果可以完全解耦 Web 控制器层和数据访问层解耦,将会给我们带来诸多便利:这会使得迁移数据存储实现更容易;也会使得代码测试更容易。「Web控制器」的职责就是真实应用的传输层:仅负责收集用户请求数据,然后将其传递给处理方。

假设你有一个类似于监控器的应用程序,该应用有很多线缆接口,你可以通过这些接口来访问监控器的功能,接口包括 HDMI,VGA,DVI 等。把互联网想象成另一个插进应用的线缆接口,显示器的大部分功能都是与线缆接口无关的、互相独立的。线缆接口只是一种传输机制,就像 HTTP 只是你程序的一种传输机制一样。所以,我们不想把传输机制(控制器)和业务逻辑混在一起。这样做的好处是很多其他的传输层比如 API 接口、移动 App 等都可以访问我们的业务逻辑。

因此,以后开发代码就别再将控制器和 Eloquent ORM 耦合在一起了,咱们来注入一个仓库类吧。

建立约定

首先,我们来定义一个接口,然后实现该接口。

interface UserRepositoryInterface
{public function all(): array;
}class DbUserRepository implements UserRepositoryInterface
{public function all(): array{return User::all()->toArray();}
}

然后,我们将该接口的实现注入到我们的控制器。

class UserController extends BaseController
{public function __construct(UserRepositoryInterface $users){$this->users = $users;}public function getIndex(){$users=$this->users->all();return View::make('users.index', compact('users'));}
}

现在,我们的控制器就完全不知道数据存储在哪了。在这里,无知是福!我们的数据可能来自 MySQL、MongoDB 或者 Redis,我们的控制器不知道也不需要知道到底用的是什么数据库,以及它们是如何存储数据的,在具体实现上有什么区别。仅仅做出了这么小小的改变,我们就可以独立于数据层来测试 Web 层了,将来如果需要的话,切换存储实现也会很容易,两者相互独立,只要调用方法名不改,我们的控制器代码不用做任何改动。

严守边界:始终牢记保持明确的责任边界,控制器和路由是作为 HTTP 和应用程序之间的中介者来提供服务的(用户浏览应用的时候,路由/控制器作为中介将其引导到对应的服务)。当编写大型应用程序时,不要将你的领域逻辑混杂在控制器或路由中。

为了巩固你对这一理念的理解,我们来写一个测试案例。首先,我们要通过 Mockery 动态模拟一个仓库类实例,并将其绑定到应用的 IoC 容器里。然后,发起一个请求,通过断言判定控制器是否正确地调用了这个仓库类:

public function testUserTest()
{$repository = \Mockery::mock(UserRepositoryInterface::class);$repository->shouldReceive('all')->once()->andReturn(['学院君']);$this->instance(UserRepositoryInterface::class, $repository);$response = $this->get('/users');$response->assertStatus(200);$response->assertViewHas('users', ['学院君']);
}

更进一步

让我们考虑另一个例子来巩固理解。当付费会员订阅的某项服务周期快结束了,可能需要去提醒用户该续费了。我们会定义两个接口,或者叫契约(这些契约使我们在更改实际实现时更加灵活),一个是支付接口,一个是通知接口:

interface BillerInterface
{public function bill(array $user, $amount);
}interface BillingNotifierInterface
{public function notify(array $user, $amount);
}

接下来我们要写一个 BillerInterface 接口的实现:

class StripeBiller implements BillerInterface
{public function __construct(BillingNotifierInterface $notifier){$this->notifier = $notifier;}public function bill(array $user, $amount){// Bill the user via Stripe...$this->notifier->notify($user, $amount);}
} 

通过将责任划分到不同类中,我们现在可以很容易将不同的通知实现类注入到账单类里面。比如,我们可以注入一个 SmsNotifier 或者 EmailNotifier。账单类只需遵守了自己的契约即可(实现了账单接口方法),不需要考虑如何实现通知功能。只要是遵守账单通知契约(接口)的类,账单类都可以用。这不仅让我们的开发维护更加灵活,而且还可以通过模拟BillingNotifierInterface 实现类来进行账单类的隔离测试,就像我们在上一个测试用例里做的那样。

面向接口开发:编写接口看上去好像要多写一些代码,但是磨刀不误砍柴工,对于大型项目而言实际上反而能提升你的开发效率,这就是软件设计领域经常说的面向接口开发,而不是面向对象开发。从测试角度来说,你不用实现任何接口,就能通过 Mockery 库模拟接口实现实例,进而测试整个后端逻辑!

前面说了这么多,回到我们的主题,我们要如何做依赖注入呢?很简单:

$biller = new StripeBiller(new SmsNotifier);

这就是一个依赖注入。账单类 StripeBiller 不用考虑如何通知用户,我们直接传递给它一个通知实现类 SmsNotifier 的实例。从代码角度来说,这可能只是个微小的变动,但这种设计模式的引入,绝对会使你的整个应用架构焕然一新:因为明确指定了类的职责边界,实现了不同层和服务之间的解耦,你的代码变得更加容易维护;此外,从面向接口编程的角度来看,代码变得更加容易测试,你只需通过模拟注入依赖即可,不同类之间的测试完全可以隔离开来。

那么 IoC 容器呢?难道依赖注入不需要 IoC 容器了么?当然不需要!在接下来的章节里面你会了解到,IoC 容器使得依赖注入更易于管理,但是容器本身不是依赖注入所必须的。只要遵循本章提出的原则,你可以在任何项目里面实现依赖注入,而不必管该项目是否提供了容器。

Laravel:依赖注入相关推荐

  1. laravel 依赖注入原理理解

    laravel框架中你所用到的依赖注入详解 Laravel中的依赖注入 面试时被问到laravel框架的依赖注入原理 一脸懵逼 下来后研究了一番 之前就听说Laravel的特点中依赖注入就是其中之一 ...

  2. laravel mysql注入_laravel中如何利用反射实现依赖注入

    依赖注入 在一个类中经常会依赖于其他的对象,先看一下经典的写法 class Foo { public $bar; public function __construct() { $this->b ...

  3. php--理解PHP的依赖注入和laravel的服务容器

    写在前面 为了了解laravel的服务容器在网上搜了许多文章,其中大多数都有其侧重点,没有很系统的一套东西以供参考,看完之后仍觉似乎少了一根把他们串起来的绳子,近期有幸拜读了陈昊的<Larave ...

  4. laravel 核心架构(1)服务容器-深入理解控制反转(IoC)和依赖注入(DI)

    1. 介绍 laravel 容器 存放的 是对象.对象的描述(类.接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 "解耦"." ...

  5. laravel服务容器-----深入理解控制反转(IoC)和依赖注入(DI)

    首先大家想一想什么是容器,字面意思就是盛放东西的东西,常见的变量,对象属性都是容器,一个容器能够装什么东西,完全在于你对这个容器的定义.有的容器不仅仅只是存文本,变量,而是对象,属性,那么我们通过这种 ...

  6. 详解 Laravel 中的依赖注入和 IoC

    作为开发者,我们一直在尝试通过使用设计模式和尝试新的健壮型框架来寻找新的方式来编写设计良好且健壮的代码.在本篇文章中,我们将通过 Laravel 的 IoC 组件探索依赖注入设计模式,并了解它如何改进 ...

  7. laravel mysql注入_详解 Laravel 中的依赖注入和 IoC

    Laravel 作为开发者,我们一直在尝试通过使用设计模式和尝试新的健壮型框架来寻找新的方式来编写设计良好且健壮的代码.在本篇文章中,我们将通过 Laravel 的 IoC 组件探索依赖注入设计模式, ...

  8. 又一个强大的PHP5.3依赖注入容器

    简单的服务容器 一个简单的 php 5.3 依赖注入容器. 项目地址:https://github.com/godruoyi/easy-container Why 目前比较流行的 PHP 容器: Pi ...

  9. PHP进阶学习之依赖注入与Ioc容器详解

    背景 在很多编程语言(例如java)开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,一旦有修改,牵扯的类会很多 ...

最新文章

  1. windows查看端口占用以及关闭相应的进程
  2. Python高级特性:切片、迭代、列表生成式、生成器与迭代器
  3. 标准C程序设计七---12
  4. Hashtable元素的删除
  5. 一条数据的HBase之旅,简明HBase入门教程-开篇
  6. 大话数据结构08:共享栈 C++
  7. nssl1321,jzoj(初中)2106-买门票【dfs,暴力,字符串】
  8. Pytorch 加载和保存模型
  9. qt如和调用linux底层驱动_擅长复杂硬件体系设计,多核系统设计,以及基于RTOS或者Linux,QT等进行相关底层驱动。...
  10. 发布锁定表头的一个思路
  11. JS设计模式——12.装饰者模式
  12. php用jquery-ajax上传多张图片限制图片大小
  13. jmeter登录配置
  14. 送给那些渐渐远离的朋友(转载)
  15. 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
  16. python函数参数为文件名_从零开始第5步:Python 函数和模块
  17. Phoca Gallery Images 去除 logo
  18. 微信小程序轮播图禁止滚动
  19. 粗糙集(Rough set) 理论
  20. 证券基金经营机构信息技术管理办法

热门文章

  1. ValidationGroup 使用
  2. 三种编程命名规范(匈牙利命名法、驼峰式命名法、帕斯卡命名法)
  3. Python高级-Django框架-01入门
  4. AVR单片机学习--mega48_88_168熔丝位含义简述
  5. 互联网新闻报道中的突发事件识别研究
  6. Docker 极简入门指南,10 分钟就能看懂~
  7. iscsi简介,安装,配置
  8. 线上讲座 “计算机科学与技术”学科导论 笔记
  9. 为什么是碳排放核算?
  10. 美国标准协会ANSI Z359防坠安全带亚马逊审核标准分析