现代PHP的新特性(4) – 编写一个接口

Code to an interface

Learning how to code to an interface changed my life as a PHP programmer,and it profoundly improved my ability to integrate third-party PHP components into my own applications.interfaces are not a new feature,but they are an important feature that you should know about and use on a daily basis.

接口编码

学习如何编写一个能够改变我们生活的PHP接口,他可以显著的提高我将第三方PHP插件融入我们自己的应用的能力.接口并不是一个新的特性,但是这是一个你必须了解的重要的特性,因为你几乎每天都要用到他.

So what is a PHP interface? An interface is a contract between two PHP object that lets one object depend not on what another object is but,instead,on what another object can do.an interface decouples our code from its dependencies,and it allows our code to depend on any third-party code implement the expected interface.we don’t care how the third-party code implements the interface;we care only that the third-party code does implement the interface.here’s a more down-to-earth example.

什么是PHP接口?接口是两个PHP对象之间的协议,使得一个对象不依赖另一个提供某种功能的对象.接口允许我们使用自己的代码依赖任何第三方代码实现我们期望的功能,我们不需要关心第三方代码是怎么编码的,我们只关心第三方代码是怎么实现接口的,这有很多实际的例子.

Let’s pretend I just arrived in miami,florida for the sunshine PHP developer conference.I need a way to get around town,so I head straight for the local car rental place.they have a tiny Hyundai compact,a subaru wagon,and(much to my surprise)a bugatti vetron. I know I need a way to get around town,and all three vehicles can help me do that.but each vehicle does so differently.the Hyundai accent is OK,but i’d like something with a bit more oomph,I don’t have kids,so the wagon has more seating than I need.i’ll take the bugatti,please.

我们假设,我刚到达阳关明媚的迈阿密参加一个PHP开发者会议.我想环游小镇,所以我顺着路走到当地的租车公司,他们有一些现代汽车,斯巴鲁,阿特兹(和我乱想的)布加迪威龙.我知道我只需要环游小镇,所以这三辆车都可以帮我实现这个目的,但是每辆车都有一些不同,现代汽车的引擎声不错,但我想要更多的活力,我没有小孩所以有狠多座位的斯巴鲁并没有什么用处,所以我选择了布加迪.

The reality is that i can drive any of there three cars because they all share a common and expected interface,each car has a steering wheel,a gas pedal,a break pedal,and turn signals,and each uses gasoline for fuel,the bugatti is probably more power than i can handle,but the driving interface is same as the Hyundai’s.because all three cars share the same expected interface,and i have the opportunity to choose my preferred vehicle(and if we’re being honest,i’d probably go with the Hyundai)

实际情况是:我们驾驶三辆中的任何一辆,因为他们都开放了同样的接口,每一辆车都有方向盘,油门踏板,刹车踏板和转向灯,而且都使用汽油为动力,布加迪驾驶时可能拥有更强的动力,但是驾驶接口都和现代汽车是一致的.所以我可以在这三辆车中选择一辆最喜欢的(老实说,我更喜欢现代汽车)

This is the exact same concept in object-oriented PHP.if I write code that expects an object of a specific class(and therefore a specific implementation).my code’s utility is inherently limited because it can only use objects of that one class,forever.however,if i write code that expects an interface,my code does not care how the interface is implemented;my code cares only that the interface is implemented.let’s drive this home with a demo.

在面向对象的PHP也是同样的道理,如果我编写代码希望一个特殊的对象(包含特殊的功能实现),那么我们的代码天生就会收到限制,因为一个类永远只有一个对象,如果编写的代码继承自接口,我们的代码就不用关心接口背后是如何执行的,我们的代码只需要关心接口本身的意义,就和开车的例子一样.

I have a hypothetical PHP class named documentstore that collects text from different source:it fetches HTML from remote URLs;it read stream resources;and it collects terminal command output.each document stored in a documentstore instance has a unique ID.example 2-6 shows the documentstore class.

我们假设PHP的一个类名为documentstore,他包含了很多代码的原型描述,如:根据URL为访客输出HTML页面,获取流资源输入,终端命令输出,每个文档都存储在documentstore对象的唯一ID中,示例2-6显示了documentstore这个类.

Example 2-6.documentstore class definition

示例2-6documentstore类的定义

Class documentstore
{
Protacted $data = [];

Public function adddocument(documentable $document)
{
$key = $document->getId();
$value = $document->getContent();
$this->data[$key] = $value;
}

Public function getDocuement()
{
Return $this->data;
}
}

How exactly does this work if the addDocument() method only accepts instances of the documentable class?that’s a good observation.however,documentable is not a class.it’s an interface,and it looks like example 2-7

如何恰当的让adducoment()方法在实例documentable类中实现?我们可以观察到documentable不是一个类而是一个接口,见实例2-7

Example 2-7 documentable interface defintion
实例2-7 documentable的接口定义

Interface documentable
{
Public function getId();

Public function getContent();
}

This interface definition says that any object implementing the documentable interface must provide a public getId() method and a public getContent() method.

So how exactly is this helpful? It’s helpful because we can create separate documentfetching classes with wildly different implementations.Example 2-8 shows an implementation that can fetch HTML from a remote URL with curl.

这个接口定义说,任何继承documentable接口的对象必须提供一个公开的getId方法和一个公开的getContent方法.

这样会为我们带来怎样的帮助?我们可以疯狂的建立很多独立实现不同功能的的documentfetching类.示例2-8展示了一个用于根据CURL来为访客输出HTML代码的实现.

Example 2-8 htmldocumetn class definition
示例2-8 htmldocument类的定义

Class htmldocument implements documentable
{
Protected $url;

Public function __constract($url)
{
$this->url = $url;
}
Public function getId()
{
Return $this->url;
}

Public function getContent()
{
$ch = curl_init();
……
Return $html;
}
}

Another implamentation(example 2-9)can read a stream resource.
另一个实现(示例2-9)可以读取流资源

Example 2-9 streamdocument class definition

Class streamdocument implements documentable
{
Protected $resource;
Protected $buffer;

Public function __construct($resource,$buffer = 4096)
{
$this->resource = $resource;
$this->buffer = $buffer;
}

Public function getId()
{
Return ‘resource_’.(int)$this->resource;;
}

Public function getContent()
{
$streamContent = ‘’;
Rewind($this->resource);
While(feof($this->resource) === false)
{
$stramContent .= fread($this->resource,$this->budder);
}
Return $streamContent;
}
}

And another implementation(example 2-10)can fetch the result of a terminal command.
另一个实现(示例2-10)可以获取终端命令的结果

Example 2-10 stramDocumetn class definition

Class commandOutputDocument implements documentable
{
Protected $command;

Public function __construct($command)
{
$this->command = $command;
}

Public function getId()
{
Return $this->command;
}

Public function getContent()
{
Return shell_exec($this->command);
}
}

Example 2-11 shows how we can use the documetnstore class with our three document-collection implementations

示例2-11展示了我们如何在documentstore类中实现三个document-collection类的实现

Example 2-11 documentstore

<?php

$documentstore = new documentStore();

//add HTML document
$htmlDoc = new HTMLdocument(‘xxx’);
$documentStore->addDocument($htmlDoc);

//add stram document
$streamDoc = new stramDocument(fopen(‘xxx’));
$documentStore->addDocument($stramDoc);

//add terminal command document
$cmdDoc = new commandOutputDocument(‘xxx’);
$documentstore->addDocument($cmdDoc);

Print_r($documentStore->getDocuments());

?>

This is really cool because the HTMLdocument,StramDocument,and CommandOutputDocument classes have nothing in common other than a common interface.

这样非常酷,因为我们把三个毫无关联的类(HTMLDocument,CommandOutputDocument,SreamDocument)用公共接口关联了起来

At the end of the day,coding to an interface create more-flexible code that delegates implementation concerns to others.many more people (e.g. Your office buddies,your open source project’s users,or developers you’ve never met) can write code that works seamlessly with your code by knowing nothing more than an interface.

建立一个接口可以为代码带来更强的适应性,并给继承他的成员建立了关系.很多人(比如 同事,使用你程序的用户或你从未谋面的共同开发者)可以通过接口将编写的代码无缝的连接在一起.