- ·上一篇文章:PHP中for循环语句的几种变型
- ·下一篇文章:PHP生成动态WAP页面
高级PHP V5 对象研究
类中不创建构造函数方法。则调用父类的构造函数并传递任何参数是您的责任,如清单 4 所示:
Listing 4. Invoking the parent class’s constructor
class SpecialDictionary extends Dictionary {
function __construct( $type, DictionaryIO $dictio, $additional ) {
// do something with $additional
parent::__construct( $type, $dictio );
}
}
抽象类和方法
虽然在父类中提供默认行为是完全合法的,但这可能不是最巧妙的方法。对于启动器,您必须依赖子类的作者来理解它们必须实现 import() 和 export(),才能在 broken 状态创建类。而且,DictionaryIO 类实际上是兄弟,而不是父子。XmlDictionaryIO 不是 DictionaryIO 的特例;相反,它是一种备选实现。
PHP V5 允许定义部分实现的类,其主要角色是为它的子女指定核心接口。这种类必须声明为抽象。
abstract class DictionaryIO {}
抽象类不能实例化。必须创建子类(即,创建继承它的类),并创建该子类的实例。可以在抽象类中声明标准和抽象方法,如清单 5 所示。抽象方法必须用 abstract 关键字限定,且必须只由一个方法签名组成。这意味着,抽象方法应包括 abstract 关键字、可选的可见度修改符、function 关键字,以及圆括号内可选的参数列表。它们不应有任何方法主体。
清单 5. 声明抽象类
abstract class DictionaryIO {
protected function path( Dictionary $dictionary,
$ext ) {
$path = Dictionary::getSaveDirectory();
$path .= DIRECTORY_SEPARATOR;
$path .= $dictionary->getType().".$ext";
return $path;
}
abstract function import( Dictionary $dictionary );
abstract function export( Dictionary $dictionary );
}
注意,path() 函数现在是受保护的。这允许来自子类的访问,但不允许来自 DictionaryIO 类型外部的访问。继承 DictionaryIO 的任何类必须实现 import() 和 export() 方法,否则就可能得到致命错误。
声明抽象方法的任何类本身必须是声明为抽象的。继承抽象类的子类必须实现在其父类或自身中声明为抽象的所有抽象方法。
清单 6 展示了具体的 DictionaryIO 类,为了简洁,此处省略了实际实现。
清单 6. 具体的 DictionaryIO 类
class SerialDictionaryIO extends DictionaryIO {
function export( Dictionary $dictionary ) {
// implementation
}
function import( Dictionary $dictionary ) {
// implementation
}
}
class XmlDictionaryIO extends DictionaryIO {
protected function path( Dictionary $dictionary, $ext ) {
$path = strtolower(parent::path( $dictionary, $ext ) );
return $path;
}
function export( Dictionary $dictionary ) {
// implementation
}
function import( Dictionary $dictionary ) {
// implementation
}
}
Dictionary 类需要一个 DictionaryIO 对象传递到它的构造函数,但它既不知道也不关心该对象是否是 XmlDictionaryIO 或 SerialDictionaryIO 的实例。它惟一知道的是给定对象继承 DictionaryIO,而且因此可以保证支持 import() 和 export() 方法。这种在运行时的类切换是面向对象编程的一个常见特性,称为多态性。
图 2 展示了 DictionaryIO 类。注意,抽象类和抽象方法用斜体表示。该图是多态性的一个好例子。它展示了 DictionaryIO 类的已定义关系是与 DictionaryIO,但 SerialDictionaryIO 或 XmlDictionaryIO 将实现该关系。
清单 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 后使用逗号分隔的列表,可以实现任意多的接口。必须实现每个接口中声明的所有方法,或者声明您的实现类抽象。 这样
Listing 4. Invoking the parent class’s constructor
class SpecialDictionary extends Dictionary {
function __construct( $type, DictionaryIO $dictio, $additional ) {
// do something with $additional
parent::__construct( $type, $dictio );
}
}
抽象类和方法
虽然在父类中提供默认行为是完全合法的,但这可能不是最巧妙的方法。对于启动器,您必须依赖子类的作者来理解它们必须实现 import() 和 export(),才能在 broken 状态创建类。而且,DictionaryIO 类实际上是兄弟,而不是父子。XmlDictionaryIO 不是 DictionaryIO 的特例;相反,它是一种备选实现。
PHP V5 允许定义部分实现的类,其主要角色是为它的子女指定核心接口。这种类必须声明为抽象。
abstract class DictionaryIO {}
抽象类不能实例化。必须创建子类(即,创建继承它的类),并创建该子类的实例。可以在抽象类中声明标准和抽象方法,如清单 5 所示。抽象方法必须用 abstract 关键字限定,且必须只由一个方法签名组成。这意味着,抽象方法应包括 abstract 关键字、可选的可见度修改符、function 关键字,以及圆括号内可选的参数列表。它们不应有任何方法主体。
清单 5. 声明抽象类
abstract class DictionaryIO {
protected function path( Dictionary $dictionary,
$ext ) {
$path = Dictionary::getSaveDirectory();
$path .= DIRECTORY_SEPARATOR;
$path .= $dictionary->getType().".$ext";
return $path;
}
abstract function import( Dictionary $dictionary );
abstract function export( Dictionary $dictionary );
}
注意,path() 函数现在是受保护的。这允许来自子类的访问,但不允许来自 DictionaryIO 类型外部的访问。继承 DictionaryIO 的任何类必须实现 import() 和 export() 方法,否则就可能得到致命错误。
声明抽象方法的任何类本身必须是声明为抽象的。继承抽象类的子类必须实现在其父类或自身中声明为抽象的所有抽象方法。
清单 6 展示了具体的 DictionaryIO 类,为了简洁,此处省略了实际实现。
清单 6. 具体的 DictionaryIO 类
class SerialDictionaryIO extends DictionaryIO {
function export( Dictionary $dictionary ) {
// implementation
}
function import( Dictionary $dictionary ) {
// implementation
}
}
class XmlDictionaryIO extends DictionaryIO {
protected function path( Dictionary $dictionary, $ext ) {
$path = strtolower(parent::path( $dictionary, $ext ) );
return $path;
}
function export( Dictionary $dictionary ) {
// implementation
}
function import( Dictionary $dictionary ) {
// implementation
}
}
Dictionary 类需要一个 DictionaryIO 对象传递到它的构造函数,但它既不知道也不关心该对象是否是 XmlDictionaryIO 或 SerialDictionaryIO 的实例。它惟一知道的是给定对象继承 DictionaryIO,而且因此可以保证支持 import() 和 export() 方法。这种在运行时的类切换是面向对象编程的一个常见特性,称为多态性。
图 2 展示了 DictionaryIO 类。注意,抽象类和抽象方法用斜体表示。该图是多态性的一个好例子。它展示了 DictionaryIO 类的已定义关系是与 DictionaryIO,但 SerialDictionaryIO 或 XmlDictionaryIO 将实现该关系。

图 2. 抽象 DictionaryIO 类及其具体子类
接口
与 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 后使用逗号分隔的列表,可以实现任意多的接口。必须实现每个接口中声明的所有方法,或者声明您的实现类抽象。 这样

