声明:本文非博主原创,而是来自《Laravel 4 From Apprentice to Artisan》阅读的翻译理解,当然不是原文翻译,可以保证90%真实性。因为是理解翻译,难免有错误,欢迎指正。欢迎转载,转载请注明出处,谢谢!InterfaceconventionstrongtypeandweaktypeWaterFowl这里不知道怎么翻译,就按照我的理解翻译成弱类型?在上一篇文章中,我介绍了依赖注入的基础知识:什么是依赖注入;如何实施;它有什么好处。在示例代码中,它还展示了如何将接口注入到类中。在继续深入之前,有必要深入了解一下界面相关的内容,因为很多PHP开发人员对界面的掌握还不够熟练。在成为PHP程序员之前,我编写了.NET。我喜欢疼痛还是什么?接口在.NET中无处不在。事实上,.NET框架的很多核心内容都是接口。还有一个好处:很多语言像.NET,比如C#和VB,都是_stronglytyped_的。通常,传入方法的本机对象必须预先定义相关的_type_。例如下面的C#代码:publicintBillUser(Useruser){this.biller.bill(user.GetId(),this.amount)}我们不仅定义了传入参数的类型,还定义了返回的类型该功能已经明确定义。C#提倡_安全类型_。函数BillUser方法传入的参数必须是一个User对象。PHP是一种_弱类型_语言。在弱类型语言中,对象上可用的方法取决于方法的使用方式,而不是方法的继承或实现位置。例如:publicfunctionbillUser($user){$this->biller->bill($user->getId(),$this->amount);}我们不需要告诉方法类型参数是什么,我们可以传入任何对象,只要它具有getId方法即可。这是弱类型编码的一个例子。如果一个东西看起来像鸭子并且叫起来也像鸭子,那么它就是鸭子。换句话说,如果一个对象像用户并且行为像用户,那么它就是一个用户对象。PHP没有强类型特性吗?答案很明确,一定有!PHP实际上是强类型和弱类型的结合。为了说明这一点,我们修改一下上面例子的代码:publicfunctionbillUser(User$user){$this->biller->bill($user->getId(),$amount);}在方法参数中,添加User契约后,可以保证传入方法的参数必须是User的实例化对象或者继承自User的实例。两种类型都各有利弊。在强类型语言中,编译器通常会提供编译和检查错误的功能,这也是非常有用的。该方法的输入和输出是显式的。同时,强类型代码看起来很生硬。比如EluquentORM中提供的动态方法whereEmailOrName就不能像C#那样明确参数和返回值的类型。这里不讨论哪个更好哪个更差,各有各的长处,但是如果不去想某个方法,肯定会坑很多。示例接口是一个合同。它不包含具体的代码实现,而是定义了对象需要实现的一系列方法。如果一个对象实现了一个接口,那么这个接口的方法肯定可以在这个实例对象中使用。通过收缩某些方法的实现,这种多态性可以保证语言的类型安全。什么是多态?多态的含义很广,可以理解为一个实体的多种形式。在本书中,我们提到了接口的各种实现。例如:UserRepositoryInterface可以有两个存储实现,MySQL和Redis,但是每一个都是UserRepositoryInter接口的实现。为了说明接口中强类型的灵活性和重要性,让我们实现以下酒店预订示例:interfaceProviderInterface{publicfunctiongetLowestPrice($location);publicfunctionbook($location);}当用户预订房间时,我们希望将此事件记录到系统中。我们在User类中添加以下方法:$this->logBookedLocation($location,$amountCharged);}}我们对参数$provider的类型进行了限制,User类可以假设book方法是可以安全调用的,这使得bookLocation的可操作性更强,我们不需要关心酒店是如何实现的过程预订房间。下面的代码可以体现这个特点:$location='Hilton,Dallas';$cheapestProvider=$this->findCheapest($location,array(newPricelineProvider,newOrbitzProvider,));$user->bookLocation($cheapestProvider,$location);伟大的!我们不需要关心哪家酒店最便宜,我们只需要将它传递到User实例中就可以成功预订房间。因为User对象要求传入的参数是继承自ProviderInterface的对象,所以以后增加更多的酒店提供者可以让我们的代码稳定运行。忘记细节请记住,接口_不实现_任何细节,它们只是定义类必须实现的方法。接口和团队开发当团队构建大型应用程序时,不同的模块会以不同的方式进行。比如有人处理数据层,有人处理前端web和controller层。前端开发项目测试自己的controller,但是后端开发进度慢。但是,如果我们能约定好接口,后端人员只需要按照接口定义来做即可:测试你自己的控制器!这样就不用担心整个应用中不同模块的开发进度,也不会影响测试用例的正常编写。从更深层次上讲,这种方法不会影响其他组件的开发,无知是福。我们不需要我们的类知道其他类如何实现它,我们只需要知道它能做什么。现在我们已经定义了接口,我们可以继续实现控制器代码:classOrderController{publicfunction__construct(OrderRepositoryInterface$orders){$this->orders=$orders;}publicfunctiongetRecent(){$recent=$this->orders->getMostRecent(Auth::user());返回View::make('orders.recent',compact('recent'));}}前端开发者可以自己实现一个“false”接口来测试应用视图中需要填充的数据。类DummyOrderRepository实现OrderRepositoryInterface{publicfunctiongetMostRecent(User$user){returnarray('Order1','Order2','Order3');}}接口实现后,我们就可以将其绑定到容器中,你可以在整个应用程序中使用他:App::bind('OrderRepositoryInterface','DummyOrderRepository');当后端开发者实现他的模块时,比如:RedisOrderRepository。我们通过修改绑定再次将其应用到项目中。接口大纲接口可用于定义项目的“骨架”。在项目组件设计阶段可以促进团队间的设计讨论。比如定义BillingNotifierInterface接口,讨论接口对应的方法。您可以在键入代码之前使用该接口定义一个好的API。
