资讯专栏INFORMATION COLUMN

Laravel深入学习3 - 接口约定

邹立鹏 / 872人阅读

摘要:难道,就没有强类型特征答案是明确的,肯定有其实是一种强类型和弱类型的结合体。强类型语言中,编译器通常提供编译检查错误的功能,它也是非常有用的。与此同时,强类型的代码看起来也是很生硬的。

声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证90%的原汁性,另外因为是理解翻译,肯定会有错误的地方,欢迎指正。

欢迎转载,转载请注明出处,谢谢!

接口约定 强类型和弱类型

Water Fowl 这里我不知道该怎么翻译,按理解就将他翻译成弱类型?

之前的文章中,涵盖了依赖注入的基础知识:什么是依赖注入;如何实现;以及他有什么好处。在举例的代码中,也展示了如何将接口注入到类之中。在我们继续深入之前,有必要深入一下接口相关的内容,因为很多PHP开发人员对接口都有相当程度的不熟练。

在我成为一个PHP程序员之前,我是写.NET的。难道我喜欢痛苦或者其他什么的么?在.NET中接口无处不在,事实上.NET框架的很多核心内容就是接口,还有一个好处:很多象.NET的语言如C#、VB等都是_强类型_的。通常,传入方法的原生对象,必须预先定义好相关的_类型_。例如下面的C#代码:

public int BillUser(User user)
{
    this.biller.bill(user.GetId(), this.amount)
}

我们不仅定义了传入参数的_类型_,函数返回的类型都是已经定义好的。C#提倡_安全类型_。函数BillUser方法传入的参数必须是User对象。

PHP是一种_弱类型_语言。在弱类型语言中,对象中可用的方法取决于其方法的使用形式,而非方法继承或者实现的位置。例如:

public function billUser($user)
{
    $this->biller->bill($user->getId(), $this->amount);
}

我们无需告诉方法类型的参数是什么,我们可以传入任意对象,只要他有getId方法就行。这就是弱类型编码的例子。如果一个东西看起来象鸭子,叫声也象,那么他就是一个鸭子。换言之,如果一个对象象user,行为也象user,那么他就是一个user对象。

难道,PHP就没有强类型特征?答案是明确的,肯定有!PHP其实是一种强类型和弱类型的结合体。为了说明这一点,我们修改下上例的代码:

public function billUser(User $user)
{
    $this->biller->bill($user->getId(), $amount);
}

在方法参数中,加入User约定后,就能确保传入方法的参数必须是User的实例化对象或者是继承自User的一个实例。

两种类型各有优劣。强类型语言中,编译器通常提供编译检查错误的功能,它也是非常有用的。方法的输入和输出都是明确的。

与此同时,强类型的代码看起来也是很生硬的。比如Eluquent ORM中提供的动态方法whereEmailOrName就不能象C#那样明确参数和返回值的类型。这里不讨论孰优孰劣,我们各取所长,但是,不假思索的死认某一种方式肯定会埋下很多坑。

示例

接口就是约定,他不包含具体的代码实现,而定义了对象需要实现的一系列的方法。如果一个对象实现了某个接口,那么这个接口的方法肯定都能在这个实例对象中使用。通过约定固化了某些方法的实现,这种_多态_就能保证语言的类型安全。

什么是多态?

多态含义很广,可以理解为一种实体的多种形式。在本书中,我们指代接口的多种实现方式。例如:UserRepositoryInterface可以有MySQL和Redis两种存储实现方式,但是每一种都是UserRepositoryInter接口的实现。

为了说明强类型在接口中的灵活和重要性,我们来实现如下一个酒店预订的例子:

interface ProviderInterface
{
    public function getLowestPrice($location);
    public function book($location);
}

当用户预订房间是,我们想将此事件记录到系统中。我们在User类中添加如下方法:

class User
{
    public function bookLocation(ProviderInterface $provider, $location)
    {
        $amountCharged = $provider->book($location);
        $this->logBookedLocation($location, $amountCharged);
    }
}

我们限定了参数$provider的类型,User类中就能假定book方法是可安全调用的,这就使得bookLocation有较强的操作性,我们不用关心酒店是如何实现房间预订这一过程。下面的代码就能体现这一特性:

$location = "Hilton, Dallas";

$cheapestProvider = $this->findCheapest($location, array(
    new PricelineProvider,
    new OrbitzProvider,
));

$user->bookLocation($cheapestProvider, $location);

赞!我们不用关心那家酒店最便宜,只需要将他传入User实例中就能成功预订房间。因为User对象要求传入的参数是继承自ProviderInterface的对象,未来添加更多的酒店提供商,都能使我们的代码稳定的运行。

忘掉细节

记住,接口_不实现_任何细节,只是简单的定义类必须实现的方法。

接口和团队开发

当团队构建大型应用时,不同的模块进程是不同的。比如,有人处理数据层,有人处理前端web、控制器层。前端开发项测试自己的控制器,但是后端人员开发进度缓慢。但是,如果我们能约定好接口,后端人员只须遵循接口定义:

interface OrderRepositoryInterface 
{
    public function getMostRecent(User $user);
}

一旦约定了接口,前端开发人员,在代码没有实现的情况下,也能测试自己的控制器!这样整个应用中就不用担心不同模块的开发进度,也不会影响到正常的测试用例的编写。更深一点来说,这种方法不会影响到其他组件的开发,做到了无知是福。我们不需要让我们的类必须知道其他类是_怎么_实现的,只需要知道他_能够_干什么。现在,我们已经定义了接口,那么我们可以继续我们控制器代码的实现了:

class OrderController 
{
    public function __construct(OrderRepositoryInterface $orders)
    {
        $this->orders = $orders;
    }
    
    public function getRecent()
    {
        $recent = $this->orders->getMostRecent(Auth::user());
        return View::make("orders.recent", compact("recent"));
    }
}

前端开发人员可以自己实现一个“假”接口,来测试应用试图中需要填充的数据。

class DummyOrderRepository implements OrderRepositoryInterface 
{
    public function getMostRecent(User $user)
    {
        return array("Order 1", "Order 2", "Order 3");
    }
}

接口实现之后,我们就能将其绑定到容器中,就能在整个应用中使用他了:

App::bind("OrderRepositoryInterface", "DummyOrderRepository");

当后端开发人员实现了他的模块,比如:RedisOrderRepository。我们再次通过修改绑定将其应用到项目之中。

接口大纲

接口在被用来定义项目“骨架”上是非常有用的。在项目组件设计阶段可以促进团队间设计讨论。比如定义BillingNotifierInterface接口,并讨论接口相应的方法,在敲代码前就能用接口定义出一套好的API。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/22952.html

相关文章

  • Laravel深入学习1 - 依赖注入

    摘要:然而,我们需要注意的是仅是软件设计模式依赖注入的一种便利的实现形式。容器本身不是依赖注入的必要条件,在框架他只是让其变得更加简便。首先,让我们探索下为什么依赖注入是有益的。继续深入让我们通过另一个示例来加深对依赖注入的理解。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证9...

    sunsmell 评论0 收藏0
  • Laravel深入学习11 - 接口分离原则

    摘要:实际上,本原则要求接口必须是粒度明确的。当你的代码不符合接口分离原则时,那也肯定违背了单一责任原则。接口分离原则本原则是指在实现类中对于接口中的方法并不强制去实现使用不到的方法。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证90%的原汁性,另外因为是理解翻译,肯定会有错误...

    lwx12525 评论0 收藏0
  • Laravel深入学习2 - 控制反转容器

    摘要:控制反转容器控制反转使依赖注入变得更加便捷。有瑕疵控制反转容器是实现的控制翻转容器的一种替代方案。容器的独立使用即使没有使用框架,我们仍然可以在项目中使用安装组件来使用的控制反转容器。在没有给定任何信息的情况下,容器是无法实例化相关依赖的。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味...

    worldligang 评论0 收藏0
  • Laravel深入学习7 - 框架的扩展

    摘要:组件扩展通常有两种方法向容器中绑定自己的接口实现痛过使用工厂模式实现的类注册自己的扩展。类库管理类以工厂模式实现,负责诸如缓存等驱动的实例化。闭包须要传入继承自和容器的实例化对象。当完成扩展之后要记住中替换成自己的扩展名称。 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证9...

    yuanxin 评论0 收藏0
  • Laravel深入学习12 - 依赖倒置原则

    摘要:在改变存储系统的情况下,必须对进行修改,违背了开放封闭原则。传统的依赖痛过倒置就能事代码变得非常灵活,易于改变 声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证90%的原汁性,另外因为是理解翻译,肯定会有错误的地方,欢迎指正。 欢迎转载,转载请注明出处,谢谢! 依赖反转原则 ...

    IamDLY 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<