什么是接口?
接口,在PHP中也像在其他的面向对象(OOP)的语言中一样,定义在一个类中有哪些方法必须实现。接口给名字和参数以记录,但是没有内容:实际的(实现)代码将会要类来提供。
<?php interface movable { function move($distance) ; } class human implements movable { public function move($distance) { $this->walk($distance); } } class dog implements movable { public function move($distance) { $this->run($distance); // dogs never walk... } } class marsrover implements movable { public function move($distance) { for($i = 0; $i < $distance / self::STEP; $i++) { $this->checkAround($distance / self::STEP); $this->rollWheels($distance / self::STEP); } } }
接口可能被用在任何对象上:事实上,创建它们的目的是为了避免类的集成层级。也许,我们可以拥有一个通用的为狗(Dog)和人(?)(Human)的动物(Animal)类,因为他们会动(move()方法),但是我们对牡蛎(Oysters)该怎么办呢?还有机器人,像火星漫步者(MarsRover)这样的东西是不可能适用的。这里,接口允许更多在类的继承层级之外的跟多数量的类。只要他们都能动(move)!
技术上说,接口将会保证实现的功能中包含需求要求的方法。这个将不会在lint时-静态代码检查(又被叫做, php -l),但是会马上在PHP定义类的时候。这意味着代码脚本必须被执行才能获得任何的验证性。
<?php interface movable { function move(); } class car implements movable { // some other methods } ?>
prompt> php -l test.php No syntax errors detected in test.php 在test.php中没有检查到语法错误
prompt> php test.php PHP Fatal error: Class car contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (movable::move) in /test.php on line 5 PHP致命错误:类Car包含一个抽象方法,所以它必须声明称抽象类或给剩下的抽象方法(movable::move)提供具体实现。见 /test.php 第5行
注意一个类可能不需要实现一个接口中定义的所有的方法,但是它必须定义成抽象的。这样,没有任何一个没有包含一个接口中定义的所有的方法的对象可以被创建。这样是很方便的:你可以运用接口于更高层次的一个抽象,而仅仅在具体的类里面定义它的方法。
不只于简单的定义
在那个层面上,接口是用来保证类提供一组明确的方法和来组织不同类的很好的工具。如果这是他们的仅有的角色,那么我们可以在那些方法写下之后马上就删掉它们;它们将仅仅是一个IDE的帮助罢了。接口其实还在其他两个PHP工具中有很好的通途:类型暗示(type hinting)和类型检查(instanceof)。
Instanceof
Instanceof检查一个类,一个子类或接口的实例。一旦一个对象被检查了,代码是能够安全的调用它定义的方法而得到统一一致的结果。
<?php foreach($aGroupOfObjects as $unknownObject) { if ($ unknownObject instanceof \movable) { $unknownObject->move(10); } } ?>
Type hint
与接口相关的Instanceof的大哥,就是类型暗示了(type hinting)。实际上,它有点像在方法签名中嵌入instanceof。这个使得早期验证方法的参数能够调用某些特定的方法变得容易。万一一个错误的对象被提供了,PHP会发出一个可以抓取(catchable)的致命错误。
<?php // within a class public function goAndReturn(movable $something) { $something->move(10); $something->move(-10); } ?>
注意到基于类方法参数的接口类型验证比基于一个类的类型的验证要轻量:这里,我们简单的只需要输入的对象拥有move()方法。接口将会仅仅检查一些方法,取决于对象的本事性质,它们可能被写成不同的方式。这里,这个方法适用于人类(human),狗类(god),火星漫步者(marsrover)或yoyo。即热我们只需要调用一个方法来工作于结果,这样是足够的。这也将会在重构代码时变得容易。
用好接口
用得好的接口是那些接口被用来做类定义和类型暗示及类型验证的接口。检查值的类型将会戏剧性地减少可能的错误。它使代码的意图更加清晰。想象一下之前的那个方法的例子如果没有类型暗示:”function goAndReturn($something)”。这个将会要在代码运行时这个方法调用的任何时候来检查。
用得好的接口是和**type hint** 或 **instanceof**一起用得接口。 确实,如果一个定义的接口从来没有用来做类型暗示或类型检查,可能也就是时候来看看它的具体实现和它的真正功能了。它可能可以被删掉,但实际上,可能在一些方法的定义中加上它的类型检查来实现代码自验证更加容易一点。试试吧!
最后,当一个接口**仅仅被实现一次**,这不是一个用得好的接口。有人可能会问为什么这么一个方法集被单独地放出来,而且它们的特征不能作为一个类来定义。也许这不是一个常用的模式而且不是真的有必要地。这个考虑,也是值得我们检查代码的。