当前位置: 首页 > 后端技术 > PHP

Laravel深度学习1——依赖注入

时间:2023-03-30 05:09:04 PHP

声明:本文非博主原创,来自《Laravel 4 From Apprentice to Artisan》阅读的翻译理解。当然不是原译,可以保证90%的原创性。另外因为是理解翻译,肯定有错误,欢迎指正。欢迎转载,转载请注明出处,谢谢!Laravel框架的基础在于其IoC容器。要真正理解框架的核心,需要对容器有一定的概念。但是需要注意的是,IoC只是一种软件设计模式:依赖注入的一种便捷实现。容器本身不是依赖注入的必要条件,框架只是让它变得更容易。首先,让我们探讨一下为什么依赖注入是有益的。考虑以下代码中的类和方法:classUserControllerextendsBaseController{publicfunctiongetIndex(){$users=User::all();返回View::make('users.index',compact('users'));}}代码简洁易懂,但不连接数据库无法测试。换句话说,EloquentORM与控制器紧密耦合。我们无法在没有数据库连接的情况下测试当前引用EloquentORM的控制器方法。此代码还违反了软件设计原则关注点分离(SoC)。简而言之:控制器知道的太多了。控制器不需要知道数据从哪里来,只关心如何访问它;它不需要关心数据库在MySQL中是否可用,而只关心数据在某处是否可用。关注点分离:每个类都应该有其单一的职责,并且这个职责完全由这个类封装。因此,将Web层(控制器)与数据层解耦将是有益的。这在我们存储和迁移数据时是有好处的,同时也让代码测试变得更容易。将“Web”视为“真实”应用程序的传输层。想象一下,该应用程序是一个具有多个电缆连接的显示器。我们可以通过HDMI、VGA或DVI访问显示功能。应用程序也可以比作将您连接到Internet的电缆。显示器的大部分主要功能都取决于电缆。而电缆只是一个传输组件,就像对您的应用程序的HTTP访问一样。所以我们不想把这部分(控制器)和应用程序逻辑混在一起。这种方法允许任何传输层(例如API或移动应用程序)访问我们的应用程序逻辑。所以,我们又注入了一个存储类来替代现有的controller和EloquentORM相结合的做法。DesignbyContracthttp://www.jdon.com/36303首先,我们定义一个接口和对应的实现:all()->toArray();}}接下来,我们将这个接口的实现注入到控制器中。类UserController扩展BaseController{publicfunction__construct(UserRepositoryInterface$users){$this->users=$users;}publicfunctiongetIndex(){$users=$this->users->all();returnView::make('users.index',compact('users'));现在,我们的控制器不知道数据存储在哪里,无知是福!我们的数据可以来自MySQL、MongoDB,甚至是Redis。我们不知道区别,也不需要关心。只需这个小改动,我们就可以将Web层与数据层分离,当然数据存储发生变化时不会影响我们。遵守界限记住要遵守职责。应用程序中的控制器和路由是对HTTP和程序交互的介绍。在大型程序中,它们不能混入您的主逻辑中。为了巩固上面的知识,我们从一个测试用例开始。首先,模拟一个库并将其绑定到IoC容器,然后确保控制器正确调用该库:publicfunctiontestIndexActionBindsUsersFromRepository(){//Arrange...$repository=Mockery::mock('UserRepositoryInterface');$repository->shouldReceive('all')->once()->andReturn(array('foo'));App::instance('UserRepositoryInterface',$repository);//行动...$response=$this->action('GET','UserController@getIndex');//断言...$this->assertResponseOk();$this->assertViewHas('users',array('foo'));您在Mockery示例中,我们使用了Mockery模拟库,它提供了一组简洁的方法来模拟您的程序。可以通过Composer安装Mockery。继续让我们通过另一个例子加深对依赖注入的理解。有一种情况,我们需要通知用户帐户的财务变化。这里我们定义了两个接口,或者约定。这些约定将使更改需求变得容易。interfaceBillerInterface{publicfunctionbill(array$user,$amount);}interfaceBillingNotifierInterface{publicfunctionnotify(array$user,$amount);}接下来,让我们实现BillerInterface接口:$notifier){$this->notifier=$notifier;}publicfunctionbill(array$user,$amount){//通过Stripe向用户收费...$this->notifier->notify($user,$amount);}}由于各个类之间的职责已经分离,所以为财务账单(billing)类注入不同的通知程序会很方便。例如,注入短信通知类SmsNotifier或邮件通知类EmailNotifier。我们的计费系统不需要考虑计费通知的实现,只需要按照约定实现加载通知即可。任何符合协议的账单都可以实现对用户财务变化的通知。另外,不仅方便我们添加,还可以单独模拟BillingNotifierInterface接口来测试计费系统。用好接口接口在写的时候看似增加了很多额外的东西,但实际上却在加速我们的开发。我们可以在不实现接口的情况下模拟开发的接口来测试整个底层逻辑。那么问题来了,如何实现依赖注入呢?$biller=newStripeBiller(newSmsNotifier);如上,简单,这就是依赖注入。你只需要将通知器传递给计费系统,不用担心通知器的使用。微小的变化可以使代码非常清晰。这种清晰的职责定义设计,让我们可以让代码维护变得简单,当然也方便模拟测试。IoC容器呢?依赖注入一定要用他吗?当然不是这里!在后面的章节中,我们会看到IoC容器只是为了更好的组织和管理依赖注入,但并不是必须的。只要遵循本章介绍的设计原则,无论是否有这样的容器,你都可以在任何项目中实现依赖注入。JAVA太多了?很多人指责PHP使用接口导致代码太冗长,太像“JAVA”了。您必须定义一个接口并实现一个类,这需要大量的输入。在小型和简单的项目中,我承认这种批评。在这样的项目中,接口是没有必要的,因为你只是自己用,以后不会改。就连架构伟大的架构师都会说“需求永远不会被确定”,但需要承认的是,tm总是有的,所以有些地方是无法改变的。接口在大型项目中非常有用,这种额外的代码是为了确保您的代码在未来的灵活性和可测试性。当你快速切换代码实现时,肯定会蒙蔽一些人的眼睛。当然我们的目的是让代码能够适应各种他妈的需求的变化。总之,我们一直提倡“干净”的架构。如果您的项目很小并且不需要遵循那么多规范,请不要害羞。代码输入有多酷。不写接口也没关系,以后再说,不是结婚买房,都是tm逼的。