摘要:然而,我们需要注意的是仅是软件设计模式依赖注入的一种便利的实现形式。容器本身不是依赖注入的必要条件,在框架他只是让其变得更加简便。首先,让我们探索下为什么依赖注入是有益的。继续深入让我们通过另一个示例来加深对依赖注入的理解。
声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证90%的原汁性,另外因为是理解翻译,肯定会有错误的地方,欢迎指正。
欢迎转载,转载请注明出处,谢谢!
依赖注入 问题所在Laravel框架的基础在于其IoC容器。要想真正了解框架的核心,需要对容器有一定的概念。然而,我们需要注意的是IoC仅是软件设计模式:依赖注入的一种便利的实现形式。容器本身不是依赖注入的必要条件,在框架他只是让其变得更加简便。
首先,让我们探索下为什么依赖注入是有益的。考虑到如下代码中的类和方法:
class UserController extends BaseController { public function getIndex() { $users = User::all(); return View::make("users.index", compact("users")); } }
代码简洁易懂,但是在没有连接到数据库的情况下,我们是无法进行测试的。换句话说, Eloquent ORM 被紧密耦合到控制器中了。在未连接数据库的情况下,我们无法测试当前引用了Eloquent ORM的控制器的方法。这段代码同样违背了软件设计原则 关注点分离(SoC) 。简言之:控制器知道的太多。控制器无需知道数据_从何而来_,只需关注如何接入;无需关心数据库在MySQL中是否可用,而只关心数据在_某处_可用。
关注点分离(Separation Of Concerns):
每个类都应有其单一的职责,并且这个职责由这个类完全封装
所以,将web层(controller)从数据层解耦分离出来会是有益的。这在我们对数据进行存储迁移时是有利的,也会使代码的测试更为简单。将“Web”认为是到“真正”应用的传输层。
想象一下,应用是一台有着多种电缆接口的显示器。我们能通过HDMI,VGA或者DVI接入显示功能。也可将应用比喻成你接入互联网的电缆。显示器的主要功能大部分依赖着电缆。而电缆仅仅是一种类似HTTP接入你应用的传输部件。所以我们不想将这部分内容(控制器)和应用逻辑糅合在一块。这种做法可以允许任何传输层,比如API或者移动应用程序来接入我们的应用逻辑。
所以,我们再次注入一个存储类,来代替现有将控制器和Eloquent ORM糅合在一块的做法。
契约式设计http://www.jdon.com/36303
首先,我们定义一个接口和相应的实现:
interface UserRepositoryInterface { public function all(); } class DbUserRepository implements UserRepositoryInterface { public function all() { 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和程序交互的中简介,在大型程序中,不能将他们糅合到你的主要逻辑中。
为了巩固上面的知识,我们从一个测试案例开始。首先,模拟一个库并绑定到IoC容器中,然后确保控制器正确的调用了该库:
public function testIndexActionBindsUsersFromRepository() { // Arrange... $repository = Mockery::mock("UserRepositoryInterface"); $repository->shouldReceive("all")->once()->andReturn(array("foo")); App::instance("UserRepositoryInterface", $repository); // Act... $response = $this->action("GET", "UserController@getIndex"); // Assert... $this->assertResponseOk(); $this->assertViewHas("users", array("foo")); }
继续深入你在模仿我么
示例中,我们使用了Mockery模拟库,它提供了一套表述简洁的方法来模仿你的程序。Mockery可通过Composer进行安装。
让我们通过另一个示例来加深对依赖注入的理解。有这样一个场景,我们需要对用户账户中发生的财务变更用进行通知。这里我们定义两个接口,或者叫约定。这些约定将会使需求变更变的很便捷。
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); } }
由于各个类之间已经进行了职责分离,为财务账单(billing)类注入不同的通知程序将会很方便。比如注入短信通知类SmsNotifier或者邮件通知类EmailNotifier。我们的账单系统不需要考虑账单通知的实现,只需要根据约定执加载通知即可。凡是遵守约定的账单,都能实现对用户财务变更的通知。此外,不光我们添加方便,我们也可以多带带模拟BillingNotifierInterface接口,来测试账单系统。
善用接口
接口写起来看似添加了很多额外的东西,实际是在加速我们的开发。我们可以在不实现接口的情况下,模拟已开发的接口,来对整个底层逻辑进行测试。
那问题来了,怎么实现依赖注入呢?
$biller = new StripeBiller(new SmsNotifier);
如上,简单吧,这就是依赖注入。只需要将通知器传入到账单系统,而不用担心通知器的使用。微小的改动就能是代码很清晰,这种清晰的职责界定设计,让我们使代码维护简单,当然也方便模拟测试。
那IoC容器是怎么一回事?依赖注入必须要用到他么?这里当然不是!在以后的章节中,我们会看到IoC容器只是为了更好的组织管理依赖注入,但它并非必须。只要遵循本章中介绍的设计原则,你可以在任何项目中实现依赖注入,也不用管是否有这样一个容器可用。
太多JAVA了吧很多人指责,在PHP中使用接口把代码变的太过冗长,太象“JAVA”。你必须定义一个接口并实现一个类,这得多敲多少代码。
在小而简的项目中,我承认这种批判。这样的项目中,接口是不必要的,因为就你自己用,以后也不会去改。即使架构上牛逼的架构师也会说“需求永远不会确定”,但是需要承认的是,总有tm那么一些地方就是改不着。
接口在大型项目中是非常有用的,这样额外的代码是为了保证未来你代码的灵活性和可测试性。当你快速切换代码实现的时候,一定会闪瞎某些人的狗眼。当然我们的目的是为了让代码能够适应各种操蛋需求的变更。
总之,我们一直提倡“简洁”架构。如果如果你的项目很小,不需要遵循这么多规范,也别不好意思。代码敲的怎么爽怎么来。如果不写接口,也行,以后再说呗,又不是结婚买房,都tm逼的。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/22563.html
摘要:划下重点,服务容器是用于管理类的依赖和执行依赖注入的工具。类的实例化及其依赖的注入,完全由服务容器自动的去完成。 本文首发于 深入剖析 Laravel 服务容器,转载请注明出处。喜欢的朋友不要吝啬你们的赞同,谢谢。 之前在 深度挖掘 Laravel 生命周期 一文中,我们有去探究 Laravel 究竟是如何接收 HTTP 请求,又是如何生成响应并最终呈现给用户的工作原理。 本章将带领大...
摘要:控制反转容器控制反转使依赖注入变得更加便捷。有瑕疵控制反转容器是实现的控制翻转容器的一种替代方案。容器的独立使用即使没有使用框架,我们仍然可以在项目中使用安装组件来使用的控制反转容器。在没有给定任何信息的情况下,容器是无法实例化相关依赖的。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味...
摘要:本文一大半内容都是通过举例来让读者去理解什么是控制反转和依赖注入,通过理解这些概念,来更加深入。这种由外部负责其依赖需求的行为,我们可以称其为控制反转。工厂模式,依赖转移当然,实现控制反转的方法有几种。 容器,字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,有这样一种容器,它存放的不是文本、数值,而是对象、对象的描...
摘要:在改变存储系统的情况下,必须对进行修改,违背了开放封闭原则。传统的依赖痛过倒置就能事代码变得非常灵活,易于改变 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证90%的原汁性,另外因为是理解翻译,肯定会有错误的地方,欢迎指正。 欢迎转载,转载请注明出处,谢谢! 依赖反转原则 ...
摘要:如果在设计中,能正确使用开放封闭原则,就能很好的规避这些问题。开放封闭原则设计原则中的开放封闭原则是指代码对扩展开放,对修改关闭。实探我们以上章中的为基础,继续来探究开放封闭原则。在进一步处理之前,要知道开闭原则并非硬规定。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证9...
阅读 3132·2021-11-10 11:35
阅读 1279·2019-08-30 13:20
阅读 1096·2019-08-29 16:18
阅读 2099·2019-08-26 13:54
阅读 2127·2019-08-26 13:50
阅读 938·2019-08-26 13:39
阅读 2451·2019-08-26 12:08
阅读 1936·2019-08-26 10:37