接口
与 Java? 编程语言应用程序一样,PHP 只支持单一继承。这意味着,类只可以继承一个父类(虽然它可能间接地继承许多祖先)。虽然这保证了清洁设计(clean design),但有时候您可能需要为一个类定义多个能力集。
使用对象的一个优点是类型可以为您提供功能的保证。Dictionary 对象总是具有 get() 方法,而不管它是 Dictionary 本身还是其子类的实例。Dictionary 的另一个特性是它对 export() 的支持。假设需要让系统中的大量其他类同样地可导出。当想要将系统的状态保存到文件中时,可以为这些完全不同的类提供各自的 export() 方法,然后聚集实例,循环通过所有实例,并为每个实例调用 export()。清单 7 展示了实现 export() 方法的第二个类。
清单 7. 实现 export() 方法的第二个类
class ThirdPartyNews { // ... }
class OurNews extends ThirdPartyNews { // ... function export() { print "OurNews export\n"; } } |
注意,本例包括约束,即新类 OurNews 继承一个叫做 ThirdPartyNews 的外部类。
清单 8 展示了聚集用 export() 方法装备的类实例的类。
清单 8. 聚集用 export() 方法装备的类实例的类
class Exporter { private $exportable = array(); function add( $obj ) { $this->exportable[] = $obj; }
function exportAll() { foreach ( $this->exportable as $obj ) { $obj->export(); } } } |
Exporter 类定义了两个方法:add(),接受要存储的对象,和 exportAll(),循环通过已存储对象,以对每个对象调用 export()。这种设计的缺点显而易见:add() 不检查所提供对象的类型,所以 exportAll() 在轻快地调用 export() 时冒了致命的风险。 此处真正有用的是 add() 方法签名中的一些类型提示。Dictionary 和 OurNews 专用于不同的根。您可以依赖 add() 方法内部的类型检查,但这并不优雅而且不固定。每次创建支持 export() 的新类型时,就需要创建一个新类型检查。
接口免去了这种麻烦。正如名称所表明的,接口定义功能而非实现。用 interface 关键字声明接口。
interface Exportable { public function export(); } |
对于抽象类,可以定义任意数目的方法签名。子类必须提供每个方法的实现。但是,与抽象类不同,接口完全不能包含任何具体方法(也就是说,任何方法的任何特性都不能与其签名分离)。 类用 implements 关键字实现接口,如清单 9 所示。
清单 9. 用 implements 关键字实现接口的类
class OurNews extends ThirdPartyNews implements Exportable { // ... function export() { print "OurNews export\n"; } }
class Dictionary implements Exportable, Iterator { function export() { //... } } |
通过在 implements 后使用逗号分隔的列表,可以实现任意多的接口。必须实现每个接口中声明的所有方法,或者声明您的实现类抽象。 这样做可以得到什么呢?现在,Dictionary 和 OurNews 对象共享类型。所有此类对象还是 Exportable。可以用类型提示和 instanceof 测试来检查它们。清单 10 展示了修改后的 Exporter::add() 方法。
清单 10. 修改后的 Exporter::add() 方法
class Exporter { private $exportable = array(); function add( Exportable $obj ) { $this->exportable[] = $obj; } //... |
接口是一个难以掌握的概念。毕竟,它们实际上并不提供任何有用代码。窍门是记住面向对象编程中类型的重要性。接口与合同类似。它借给类一个将类放置到位的名称,反过来,该类保证特定方法将可用。此外,使用 Exportable 对象的类既不知道也不关心调用 export() 时发生的行为。它只知道它可以安全地调用该方法。
图 3 显示了 Exportable 接口与其实现类之间的关系。注意到 Exporter 与 Exportable 接口而非具体实现有使用关系。接口关系用虚线和开箭头表示。
图 3. 接口及其实现类 |
结束语 本文支持使用 PHP V5 中类型的价值。对象类型允许将系统中的组件相互分离,从而得到可重用、可扩展和可伸缩的代码。抽象类和接口帮助您基于类类型设计系统。 客户机类可被编码为只需要抽象类型,而把实现策略和结果留给在运行时传递给它们的具体类实例。也就是说,Dictionary 既不局限于序列化数据,也不局限于 XML。如果必须支持一种新格式,Dictionary 将不需要任何进一步的开发。它与保存数据以及从文件系统加载数据和将数据加载到文件系统的机制完全无关。Dictionary 只知道它必须具有一个 DictionaryIO 对象,从而保证 export() 和 import() 的功能。
如果类保证了接口,您必须能够保证类。虽然 instanceof 功能提供了一种检查类型的好方法,但您还可以通过在参数列表中使用类型提示,来将对象类型检查滚动到方法签名自身中。
查看本文来源