高级PHP V5 对象研究

对象|高级

高级PHP V5 对象研究
本文介绍了PHP V5一些更高级的面向设计的特性。其中包括各种对象类型,它们允许将系统中的组件相互分离,创建可重用、可扩展、可伸缩的代码。

  领会暗示 

  首先介绍一下对象类型和类型提示的优点。一个类定义一种类型。从该类实例化的任何对象属于该类定义的类型。所以,使用 Car 类创建 Car 对象。如果 Car 类继承 Vehicle 超类,则 Car 对象还将是一个 Vehicle 对象。这反映了我们在现实世界中分类事物的方法。但正如您将看到的,类型不仅仅是分类系统元素的有用方法。类型是面向对象编程的基础,因为类型是良好一致的行为的保证。许多设计技巧来自该保证。

  “开始了解 PHP V5 中的对象”展示对象为您保证了接口。当系统传递 Dictionary 对象时,您可以确定它具有 $translations 数组和 summarize() 方法。相反,关联数组不提供相同级别的确定性。要利用类提供的清晰接口,需要知道您的对象实际上是 Dictionary 的一个实例,而不是某个 imposter。可以用 instanceof 操作符来手动验证这一点,该操作符是 PHP V5 引入的介于对象实例和类名之间的一个便捷工具。

   instanceof Dictionary

  如果给定对象是给定类的实例,则 instanceof 操作符解析为真。在调用方法中第一次遇到 Dictionary 对象时,可以在使用它之前检查它的类型。

if ( $en instanceof Dictionary ) {
 print $en->summarize();
}

  但是,如果使用 PHP V5 的话,可以将对象类型检查构建到类或方法声明中。

  在“开始了解 PHP V5 中的对象”中,重点介绍两个类:Dictionary,它存储术语和翻译, DictionaryIO,它将 Dictionary 数据导出(导入)自(至)文件系统。这些特性使得将 Dictionary 文件发送到第三方翻译器变得容易,第三方翻译器可以使用自己的软件来编辑数据。然后,您可以重新导入已处理的文件。清单 1 是 Dictionary 类的一个版本,它接受一个 DictionaryIO 对象,并将其存储以备将来使用。

  清单 1. 接受 DictionaryIO 对象的 Dictionary 类的一个版本

class Dictionary {
 public $translations = array();
 public $type ="En";
 public $dictio;

 function addDictionaryIO( $dictio ) {
  $this->dictio=$dictio;
 }

 function export() {
  if ( $this->dictio ) {
   $this->dictio->export( $this );
  }
 }
}

class DictionaryIO {
 function export( $dict ) {
  print "exporting dictionary data "."($dict->type)\n";
 }
}

$en = new Dictionary();
$en->addDictionaryIO( new DictionaryIO() );
$en->export();

// output: 
// dumping dictionary data (En)

  DictionaryIO 类具有单个方法 export(),它接受一个 Dictionary 对象,并使用它来输出假消息。现在,Dictionary 具有两个新方法:addDictionaryIO(),接受并存储 DictionaryIO 对象; export(),使用已提供的对象导出 Dictionary 数据 —— 或者是在完全实现的版本中。

  您可能会疑惑为什么 Dictionary 对象不仅实例化自己的 DictionaryIO 对象,或者甚至在内部处理导入导出操作,而根本不求助于第二个对象。一个原因是您可能希望一个 DictionaryIO 对象使用多个 Dictionary 对象,或者希望存储该对象的单独引用。另一个原因是通过将 DictionaryIO 对象传递给 Dictionary,可以利用类切换或 多态性。换句话说,可以将 DictionaryIO 子类(比如 XmlDictionaryIO)的实例传递给 Dictionary,并更改运行时保存和检索数据的方法。

  图 1 显示了 Dictionary 和 DictionaryIO 类及其使用关系。

正如所显示的,没有什么阻止编码器将完全随机的对象传递给 addDictionaryIO()。只有在运行 export() 时,才会获得一个类似的错误,并发现已经存储在 $dictio 中的对象实际上并没有 export() 方法。使用 PHP V4 时,必须测试本例中的参数类型,以绝对确保编码器传递类型正确的对象。使用 PHP V5 时,可以部署参数提示来强制对象类型。只将所需的对象类型添加到方法声明的参数变量中,如清单 2 所示:

  清单 2. 将对象类型添加到方法声明的参数变量中

function addDictionaryIO( DictionaryIO $dictio ) {
 $this->dictio=$dictio;
}

function export() {
 if ( $this->dictio ) {
  $this->dictio->export( $this );
 }
}

  现在,如果客户机编码器试图将类型错误的对象传递给 addDictionaryIO(),PHP 引擎将抛出一个致命错误。因此,类型提示使得代码更安全。不幸的是,提示仅对对象有效,所以不能在参数列表中要求字符串或整数。必须手动测试这些原类型。

  即使可以保证 addDictionaryIO() 将获得正确的对象类型,但不能保证该方法被首先调用。export() 方法测试 export() 方法中 $dictio 属性的存在,从而避免错误。但您可能希望更严格一些,要求 DictionaryIO 对象传递给构造函数,从而确保 $dictio 总是被填充。

  调用覆盖方法

  在清单 3 中,XmlDictionaryIO 集成 DictionaryIO。而 DictionaryIO 写入并读取序列化数据,XmlDictionaryIO 操作 XML,可以与第三方应用程序共享。XmlDictionaryIO 可以覆盖其父方法(import() 和 export()),也可以选择不提供自己的实现(path())。如果客户机调用 XmlDictionaryIO 对象中的 path() 方法,则在 DictionaryIO 中实现的 path() 方法被调用。

  事实上,可以同时使用这两种方法。可以覆盖方法并调用父实现。为此,使用新关键字 parent。用范围解析操作符和所讨论方法的名称来使用 parent 。例如,假设需要 XmlDictionaryIO 使用当前工作目录(如果有一个可用)中叫做 xml 的目录;否则,它应使用由父 DictionaryIO 类生成的默认路径,如清单 3 所示:

  清单 3. XmlDictionaryIO 使用 xml 目录或由 DictionaryIO 类生成的默认路径

class XmlDictionaryIO extends DictionaryIO {

 function path( Dictionary $dictionary, $ext ) {
  $sep = DIRECTORY_SEPARATOR;
  if ( is_dir( ".{$sep}xml" ) ) {
   return ".{$sep}xml{$sep}{$dictionary->getType()}.$ext";
  }
  return parent::path( $dictionary, $ext );
 }
// ...

  可以看到,该方法检查本地 xml 目录。如果该测试失败,则它使用 parent 关键字指派给父方法。

子类和构造函数方法

  parent 关键字在构造函数方法中尤其重要。如果在子类中不定义构造函数,则 parent 构造函数代表您被显式调用。如果在子类中不创建构造函数方法。则调用父类的构造函数并传递任何参数是您的责任,如清单 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 将实现该关系。


图 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 后使用逗号分隔的列表,可以实现任意多的接口。必须实现每个接口中声明的所有方法,或者声明您的实现类抽象。 这样做可以得到什么呢?现在,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 接口而非具体实现有使用关系。接口关系用虚线和开箭头表示。

结束语
  本文支持使用 PHP V5 中类型的价值。对象类型允许将系统中的组件相互分离,从而得到可重用、可扩展和可伸缩的代码。抽象类和接口帮助您基于类类型设计系统。 客户机类可被编码为只需要抽象类型,而把实现策略和结果留给在运行时传递给它们的具体类实例。也就是说,Dictionary 既不局限于序列化数据,也不局限于 XML。如果必须支持一种新格式,Dictionary 将不需要任何进一步的开发。它与保存数据以及从文件系统加载数据和将数据加载到文件系统的机制完全无关。Dictionary 只知道它必须具有一个 DictionaryIO 对象,从而保证 export() 和 import() 的功能。
  如果类保证了接口,您必须能够保证类。虽然 instanceof 功能提供了一种检查类型的好方法,但您还可以通过在参数列表中使用类型提示,来将对象类型检查滚动到方法签名自身中

时间: 2024-11-02 01:55:53

高级PHP V5 对象研究的相关文章

smarty高级特性之对象的使用方法_php实例

本文实例讲述了smarty高级特性之对象的使用方法.分享给大家供大家参考,具体如下: <?php include_once('smarty.inc.php'); class Dog{ public $name; public function sayHello(){ echo 'hello'; } } $dog1=new Dog(); $dog1->name="first dog"; $smarty->assign("dog",$dog1); $s

symbian入门系列:applicatoin对象研究

通常我们使用的application framework需要以下几个类:application,document,appui,view,container.每个对象负责不同的功能.这里首先从application对象开始分析. 1. Application 分析 ------------------------- 继承关系: CEikApplication -->CApaApplication -->CBase ------------------------- application对象是整

深入JavaScript高级程序设计之对象、数组(栈方法,队列方法,重排序方法,迭代方法)_javascript技巧

继承是OO语言中的一个最为人津津乐道的概念. 许多OO语言都支持两种继承方式:接口继承和实现继承. 接口继承只继承方法签名,而实现继承则继承实际的方法. 如其所述,由于函数没有签名,在ECMAScript中无法实现接口继承. ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的. 1.使用对象字面量定义对象 var person={}; 使用这种方式创建对象时,实际上不会调用Object构造函数. 开发人员更喜欢对象字面量的语法. 2.有时候需要传递大量可选参数的情形时,一

JavaScript 类的定义和引用 JavaScript高级培训 自定义对象_js面向对象

一,概述 在Java语言中,我们可以定义自己的类,并根据这些类创建对象来使用,在Javascript中,我们也可以定义自己的类,例如定义User类.Hashtable类等等. 目前在Javascript中,已经存在一些标准的类,例如Date.Array.RegExp.String.Math.Number等等,这为我们编程提供了许多方便.但对于复杂的客户端程序而言,这些还远远不够. 与Java不同,Java2提供给我们的标准类很多,基本上满足了我们的编程需求,但是Javascript提供的标准类很

Java面向对象高级--继承的进一步研究

1.子类对象的实例化过程 class Person{ // 定义Person类 private String name ; // 定义name属性 private int age ; // 定义age属性 public Person(){ System.out.println("父类Person中的构造.") ; } public void setName(String name){ this.name = name; } public void setAge(int age){ th

Direct2D教程VIII——几何(Geometry)对象的运算,本系列的终结篇

目前博客园中成系列的Direct2D的教程有 1.万一的 Direct2D 系列,用的是Delphi 2009 2.zdd的 Direct2D 系列,用的是VS中的C++ 3.本文所在的 Direct2D教程 系列,用的是VS2010的Visual Basic语言(可以很方便的转为C#),基于Windows API Code Pack 1.1.   还有官方的说明文档 Direct2D ,用的是C++.   本系列的前几篇文章: Direct2D教程I--简介及首个例子 Direct2D教程II

javascript中如何正确将日期(Date)字符串转换为日期(Date)对象?

因近日一个项目中要在客户端判断用户输入的日期字符串的大小,所以对日期字符串转日期对象研究了一下,测试代码如下:   <script. type="text/javascript"> var sDate1 = "2008/04/02"; var sDate2 = "2005/03/01"; var oDate1 = new Date(sDate1); var oDate2 = new Date(sDate2); if (oDate1 &

国务院关于印发 新一代人工智能发展规划的通知

国务院关于印发 新一代人工智能发展规划的通知 国发[2017]35号 各省.自治区.直辖市人民政府,国务院各部委.各直属机构: 现将<新一代人工智能发展规划>印发给你们,请认真贯彻执行. 国务院 2017年7月8日 (此件公开发布) 新一代人工智能发展规划 人工智能的迅速发展将深刻改变人类社会生活.改变世界.为抢抓人工智能发展的重大战略机遇,构筑我国人工智能发展的先发优势,加快建设创新型国家和世界科技强国,按照党中央.国务院部署要求,制定本规划. 一.战略态势 人工智能发展进入新阶段.经过60

CNN实现“读脑术”,成功解码人脑视觉活动,准确率超50%

研究人员开发出以人脑为模型的深度学习算法,来破解人类大脑.相关研究发表在最新一期Cerebral Cortex,研究人员构建了一个大脑如何解码信息的模型,根据参与者的大脑活动,该模型能够以50%的精确度预测她所看到的东西. 人工智能让我们离科幻小说里的"读脑机器"更近了一步.现在,研究人员开发出以人脑为模型的深度学习算法,来破解人类大脑.首先,他们建立了一个大脑如何解码信息的模型.三名女性花费了数小时观看几百条短视频,功能性核磁共振机器测量了视觉皮层和其他地方的活动信号.一个用于图像处