From Apprentice To Artisan 翻译 05

2014/10/29 21:28
阅读数 6.2K


Interface As Contract 接口约定

Strong Typing & Water Fowl 强类型和小鸭子

In the previous chapters, we covered the basics of dependency injection: what it is; how it is accomplished; and several of benefits. The examples in the previous chapters also demonstrated the injection of interface into our classes, so before proceeding further, it will be beneficial to talk about interfaces in depth, as many PHP developers are not familiay with them.

在之前的章节里,涵盖了依赖注入的基础知识:什么是依赖注入;如何实现依赖注入;依赖注入有什么好处。 之前章节里面的例子也模拟了将interface注入到classes里面的过程。在我们继续学习之前,有必要深入讲解一下接口,而这正是很多PHP开发者所不熟悉的。

Before I was a PHP programmer, I was a .NET programmer. Do I love pain or what? In .NET, interfaces are everywhere. In fact, many interfaces are defined as part of the core .NET framework itself, and for good reason: many of the .NET languages, such as C# and VB.NET, are strongly typed. In general, you must define the type of object or primitive you will be passing into a method. For example, consider the following C# method:

在我成为PHP程序员之前,我是写.NET的。 你觉得我是M么?在.NET里可到处都是接口。 事实上很多接口是定义在.NET框架核心中了,一个好的理由是:很多.NET语言比如C#和VB.NET都是强类型的。 也就是说,你在给一个函数传值,要么传原生类型对象,要么就必须给这个对象一个明确的类型定义。比如考虑以下C#方法:

<!-- lang: c# -->
public int BillUser(User user)
    this.biller.bill(user.GetId(), this.amount)

Note that we were forced to define not only what type of arguments we will be passing to the method, but also what the method itself will be returning. C# is encouraging type safety. We will not be allowed to pass anything other than an User object into out BillUser method

注意在这里, 我们不仅要定义传进去的参数是什么类型的,还要定义这个方法返回值是什么类型的。 C#鼓励类型安全。除了指定的User对象,它不允许我们传递其他类型的对象到BillUser方法中。

However, PHP is generally a duck typed language. In a duck typed language, an object's avaliable methods determine the way it may be used, rather than its inheritance from a class or implementation of an interface. Let's look at an example:

然而PHP是一种鸭子类型的语言。 所谓鸭子类型的语言, 一个对象可用的方法取决于使用方式, 而非这个方法从哪儿继承或实现。来看个例子:

<!-- lang:php -->
public function billUser($user)
    $this->biller->bill($user->getId(), $this->amount);

In PHP, we did not have to tell the method what type of argument to expect. In fact, we can pass any type, so long as the object responds to the getId method. This is an example of duck typing. If the object looks like a duck, quacks like a duck, it must be a duck. Or, in this case, if the object looks like a user, and acts like a user, it must be one.

在PHP里面,我们不必告诉一个方法需要什么类型的参数。 实际上我们传递任何类型的对象都可以,只要这个对象能响应getId的调用。这里有个关于鸭子类型(下文译作:弱类型)的解释:如果一个东西看起来像个鸭子,叫声也像鸭子叫,那他就是个鸭子。 换言之在程序里,一个对象看上去是个User,方法响应也像个User,那他就是个User。

But, does PHP have any strongly typed style features? Of course! PHP has a mixture of both strong and duck typed constructs. To illustrate this, let's re-write our billUser method:


<!-- lang:php -->
public function billUser(User $user)
    $this->biller->bill($user->getId(), $amount);

After adding the User type-hint to our method signature, we can now gurantee that all objects passed to billUser either are a User instance, or extend the User class.

给方法加上了加上了User类型提示后, 我们可以确信的说所有传入billUser方法的参数,都是User类或是继承自User类的一个实例。

There are advangates and disadvantages to both typing systems. In strongly typed languages, the compiler can often provide you with through compile-time error checking, which is extremely helpful. Method inputs and outputs are also much more explicit in a strongly typed language.

强类型和弱类型各有优劣。 在强类型语言中, 编译器通常能提供编译时错误检查的功能,这功能可是非常有用的。方法的输入和输出也更加明确。

As the same time, the explicit nature of strong typing can make it rigid. For example, dynamic methods such as whereEmailOrName that the Eloquent ORM offers would be impossible to implement in a strongly typed language like C#. Try to avoid heated discussions on which paradigm is better, and remember the advantages and disadvantages of each. It is not "wrong" to use the strong typing avaliable in PHP, nor is it wrong to use duck typing. What is wrong is exclusively using one or the other without considering the particular problem you are trying to solve.

与此同时,强类型的特性也使得程序僵化。比如Eloquent ORM中,类似whereEmailOrName的动态方法就不可能在C#这样的强类型语言里实现。我们不讨论强类型弱类型哪种更好,而是要记住他们分别的优劣之处。在PHP里面使用强类型标记不是错误,使用弱类型特性也不是错误。但是不加思索,不管实际情况去使用一种模式,这么固执的使用就是错的。

A Contract Example 约定的范例

Interfaces are contracts. Interfaces do not contain any code, but simply define a set of methods that an object must implement. If an object implements an interface, we are guranteed that every method defined by the interface is valid and callable on that object. Since the contract gurantees the implementation of certain methods, type safety becomes more flexible via polymorphism.


Poly what? 多什么肽?

Polymorphism is a big word that essentially means an entity can have multiple forms. In the context of this book, we mean that an interface can have multiple implementations. For example, a UserRepositoryInterface could have a MySQL and a Redis implementation, and both implementations would qualify as a UserRepositoryInterface instance.


To illustrate the flexibility that interfaces introduce into strongly typed languages, let's write some simple code that books hotel rooms. Consider the following interface:


<!-- lang:php -->
interface ProviderInterface{
    public function getLowestPrice($location);
    public function book($location);

When our user books a room, we'll want to log that in our system, so let's add a few methods to our User class:


<!-- lang:php -->
class User{
    public function bookLocation(ProviderInterface $provider, $location)
        $amountCharged = $provider->book($location);
        $this->logBookedLocation($location, $amountCharged);

Since we are type hinting the ProviderInterface, our User can safely assume that the book method will be available. This gives us the flexibility to re-use our bookLocation method regardless of the hotel provider the user prefers. Finally, let's write some code that harnesses this flexibility:


<!-- lang:php -->
$location = 'Hilton, Dallas';

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

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

Wonderful! No matter what provider is the cheapest, we are able to simply pass it along to our User instance for booking. Since our User is simply asking for an object instances that abides by the ProviderInterface contract, our code will continue to work even if we add new provider implementations.


Forget The Details 忘掉细节

Remember, interfaces don't actually do anything, They simply define a set of methods that an implementing class must have.



0 收藏
0 评论
0 收藏