《深入PHP:面向对象、模式与实践》(二)

第4章 高级特性

本章内容提要:

  • 静态属性和方法:通过类而不是对象来访问数据和功能
  • 抽象类和接口:设计和实现分离
  • 错误处理:异常
  • Final类和方法:限制继承
  • 拦截器方法:自动委托
  • 析构方法:对象销毁前的清理工作
  • 克隆对象:创建对象的副本
  • 把对象解析成字符串:创建摘要型方法
  • 回调:用匿名函数为组件添加功能

4.1  静态属性和方法

静态属性和方法的声明:使用 static 关键字。

静态方法只能访问静态属性,不能访问普通属性。普通属性是属于对象的。

静态属性和方法的访问:使用 :: 来连接类名和静态元素。

一个类使用 parent 关键字来访问父类。在当前类(不是子类)访问静态元素,使用 self 关键字。在类外部,使用类名访问静态元素。

只有在使用 parent 关键字调用方法的时候,才能对一个非静态方法进行静态形式的调用。

静态元素的特性:

  • 在任何地方都可用(假设你可以访问该类)
  • 类的每个实例都可以访问类中定义的静态元素
  • 不需要对象就能访问静态元素

书中介绍了一个静态方法,该方法接受原始数据,然后返回对象,就像“工厂”一样。

4.2  常量属性

常量属性使用 const 关键字来声明。常量属性不以美元符号开头,只能用大写字母来命名。

常量属性只包含基本数据类型的值。只能通过类而不能通过类的实例访问常量属性。引用常量属性时不需要用美元符号作为前导符。

当需要在类的所有实例中都能访问某个属性,并且属性值无需改变时,应该使用常量。

4.3  抽象类

用 abstract 关键字定义一个抽象类。可以创建抽象类的属性和方法。抽象类不能被实例化。

大多数情况下,抽象类至少包含一个抽象方法。抽象方法用 abstract 关键字声明,不能有具体内容,要以分号结束而不是方法体结束。

抽象类的每个子类都必须实现抽象类中的所有抽象方法,或者把它们自身也声明为抽象方法。

新的实现方法的访问控制不能比抽象方法的访问控制更严格。这些方法的调用方式也必须匹配,即类型和所需参数数量必须一致。

4.4  接口

抽象类提供了具体实现的标准,而接口则是纯粹的模板。

接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。

接口中定义的所有方法都必须是公有,这是接口的特性。

要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。

  • 实现多个接口时,接口中的方法不能有重名。
  • 接口也可以继承,通过使用 extends操作符。
  • 类要实现接口,必须使用和接口中所定义的方法完全一致的方式。否则会导致致命错误。

接口中也可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。

接口加上类型约束,提供了一种很好的方式来确保某个对象包含有某些方法。

本小节定义了一个 Chargeable 接口,再用 ShopProduct 类来实现,同时在其方法的参数中使用了类型约束。

4.5  延迟静态绑定:static 关键字

self:: 的限制:使用 self:: 或者 __CLASS__ 对当前类的静态引用,取决于定义当前方法所在的类。

延迟静态绑定:使用预留的 static 关键字,用于在继承范围内引用静态调用的类。

  • 在非静态环境下,所调用的类即为该对象实例所属的类。由于 $this-> 会在同一作用范围内尝试调用私有方法,而 static:: 则可能给出不同结果。另一个区别是 static:: 只能用于静态属性。
  • 延迟静态绑定的解析会一直到取得一个完全解析了的静态调用为止。另一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。

应用场景:可以为超类引入 组 的概念。

4.6  错误处理

本节举了一个简单的 conf 类的例子。这个类没有任何处理配置信息不存在或不可写的策略,只是假定了 XML 文档的格式正确并包含了需要的元素。这是很不明智的。

本节重点强调:应该把错误处理的责任集中放在类的内部,而不能依赖调用该类的程序员和外部代码。

4.7  异常

PHP5 引入了异常(exception)。

异常是从 PHP5 内置的 Exception 类(或其子类)实例化得到的特殊对象。

Exception 类的构造方法接受两个可选参数:消息字符串和错误代码。

1. 抛出异常

可以联合使用 throw 关键字和 Exception 对象来抛出异常。这会停止执行当前方法,并负责将错误返回给调用代码。书中重写了前面 conf 类的构造方法和 write() 方法。

如果调用可能会抛出异常的方法,那么可以把调用语句放在 try 子句中。try 子句由关键字 try 及其后面的括号组成。try 字句必须跟着至少一个 catch 子句才能处理错误。

2. 异常的子类化

通过继承 Exception 类,可以创建用户自定义的异常类。

定义多个 catch 子句时,只需一个 try 子句。调用哪一个 catch 子句取决于抛出异常的类型和参数中类的类型提示。

书中自定义了一个继承自 Exception 类的 XmlException 类、FileException 类、ConfException 类,并在 Conf 类中使用了这些自定义类。最后写一个 Runner 类来捕捉 Conf 类的构造方法可能抛出3个异常中的任意一个,该类使用了多个 catch 子句,并注意把 Exception 异常类型放在最后一个 catch 子句中。另外的一个技巧是,可以抛出包装了当前异常的新异常。

当异常没有被客户端代码捕获,异常会被再次抛出,客户的调用代码会优先捕获它。如果异常最后也没有被捕获,将会引发致命错误。所以当抛出异常时,要强制要求客户端代码对它进行处理。

4.7  Final 类和方法

final 类不能被继承,即不能有子类。final 方法不能被覆写。final 关键字应该放在其他修饰词(例如 protected 或 static)之前。

慎重使用 final 关键字。

4.8  使用拦截器

PHP 提供了内置的拦截器方法。

  •  __get( $property ):访问未定义的属性时被调用
  • __set( $property, $value ):给未定义的属性赋值时被调用
  • __isset( $property ):对未定义的属性调用 isset() 时被调用
  • __unset( $property ):对未定义的属性调用 unset() 时被调用
  • __call( $method, $arg_array ):调用未定义的方法时被调用

书中写了一个 Person 类,说明了如何使用拦截器方法。还写了一个 PersonWriter 类,用来说明 __call() 方法如何实现委托。过多地使用委托,会使代码变得不清晰。应慎重使用拦截器方法,并且最好附上说明文档。

4.9  析构方法

析构方法__destruct():只在对象被垃圾收集器收集前(即对象从内存中删除之前)自动调用。可以使用析构方法进行最后必要的清理操作。

书中给 Person 类添加了析构方法,并说明应该谨慎使用析构方法等魔术方法。

4.10  使用 __clone() 复制对象

在PHP中,对象的赋值和引用都是通过引用进行的。要获得一个对象的副本,PHP提供了clone关键字。clone使用“值复制”方式新生成一个对象。

在对象上调用clone时,可以通过实现__clone()方法来控制我们需要复制什么。当在一个对象上调用clone关键字时,其__clone()方法就会被自动调用。__clone()是在复制得到的对象上运行的,而不是在原始对象上运行的。

在复制对象属性时只复制引用,并不复制引用的对象。如果不希望对象属性在被复制之后被共享,那么可以显式得在__clone()方法中复制指向的对象。

4.11 定义对象的字符串值

通过实现__toString()方法,可以控制输出对象的字符串值的格式。__toString()方法应当返回一个字符串值。当把对象传递给print或echo时,会自动调用这个方法,并用方法的返回值来替代默认的输出内容。

4.12 回调、匿名函数和闭包

 

posted on 2017-05-15 18:11  afee666  阅读(346)  评论(0编辑  收藏  举报

导航