php面向对象编程_第1页
php面向对象编程_第2页
php面向对象编程_第3页
php面向对象编程_第4页
php面向对象编程_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

PHP 面向对象编程 第一节 面向对象编程 面向对象编程被设计来为大型软件项目提供解决方案,尤其是多人合作 的项目. 当源代码增长到一万行甚至更多的时候,每一个更动都可能导致不希 望的副作用. 这种情况发生于模块间结成秘密联盟的时候,就像第一次世界大 战前的欧洲. /haohappy 注:喻指模块间的关联度过高,相互依赖性太强.更动一个模块导致 其它模块也必须跟着更动. 想像一下,如果有一个用来处理登录的模块允许一个信用卡处理模块来 分享它的数据库连接. 当然出发点是好的,节省了进行另一个数据库连接的支 出.然而有时,登录处理模块改变了其中一个变量的名字,就可能割断了两者间 的协议.导致信用卡模块的处理出错,进而导致处理发票的模块出错. 很快地, 体系中所有无关的模块都可能由此出错. 因此,我觉得有点戏剧性地,绝大多数程序员都对耦合和封装心存感激. 耦合是两个模块间依赖程度的量度. 耦合越少越好.我们希望能够从已有的项 目中抽走一个模块并在另一个新项目中使用. 我们也希望在某个模块内部大规模的更动而不用担心对其他模块的影响. 封装的原则可以提供这个解决方案.模块被看待成相对独立,并且模块间的数 据通信通过接口来进行. 模块不通过彼此的变量名来窥探另一个模块,它们通 过函数来礼貌地发送请求。 封装是你可以在任何编程语言中使用的一个原则. 在 PHP 和许多面向 过程的语言中,可以偷懒是很有诱惑的.没有什么可以阻止你通过模块来构建 一个假想的 WEB. 面向对象编程是使程序员不会违背封装原则的一种方法。 在面向对象编程中,模块被组织成一个个对象. 这些对象拥有方法和属性. 从抽象的角度来看,方法是一个对象的所做的动作,而属性是对象的特性.从编 程角度来看,方法就是函数而属性是变量. 在一个理想化的面向对象体系中, 每个部份都是一个对象. 体系由对象及对象间通过方法来形成的联系构成。 一个类定义了对象的属性. 如果你在烘烤一组甜饼对象,那么类将会是甜 饼机. 类的属性和方法是被调用的成员. 人们可以通过说出数据成员或者方 法成员来表达。 每种语言提供了不同的途径来访问对象. PHP 从 C+中借用概念,提供一 个数据类型用来在一个标识符下包含函数和变量。最初设计 PHP 的时候, 甚至 PHP3 被开发出时, PHP 并不打算提供开发超过 10 万行代码的大型项 目的能力。随着 PHP 和 Zend 引擎的发展,开发大型项目变得有可能,但无 论你的项目规模多大,用类来书写你的脚本将可以让代码实现重用。这是一 个好主意,特别当你愿意与别人分享你的代码的时候。 有关对象的想法是计算机科学上最令人兴奋的概念之一。开始很难掌握 它,但我可以保证,一旦你掌握了它,用它的思维来思考将会非常自然。 第二节 对象模型 PHP5 有一个单重继承的,限制访问的 ,可以重载的对象模型. 本章稍后会 详细讨论的”继承 ”,包含类间的父-子关系. 另外,PHP 支持对属性和方法的限 制性访问. 你可以声明成员为 private,不允许外部类访问. 最后,PHP 允许一 个子类从它的父类中重载成员。 file:/haohappy 注:PHP4 中没有 private,只有 public.private 对于更好地实 现封装很有好处。 PHP5 的对象模型把对象看成与任何其它数据类型不同,通过引用来传递. PHP 不要求你通过引用(reference)显性传递和返回对象. 在本章的最后将会 详细阐述基于句柄的对象模型. 它是 PHP5 中最重要的新特性。 有了更直接的对象模型,基于句柄的体系有附加的优势: 效率提高, 占用 内存少,并且具有更大的灵活性。 在 PHP 的前几个版本中 ,脚本默认复制对象 .现在 PHP5 只移动句柄 ,需要 更少的时间. 脚本执行效率的提升是由于避免了不必要的复制. 在对象体系 带来复杂性的同时,也带来了执行效率上的收益. 同时,减少复制意味着占用 更少的内存,可以留出更多内存给其它操作,这也使效率提高. file:/haohappy 注:基于句柄,就是说两个对象可以指向同一块内存,既减 少了复制动作,又减少对内存的占用。 Zand 引擎 2 具有更大的灵活性 . 一个令人高兴的发展是允许析构-在对 象销毁之前执行一个类方法. 这对于利用内存也很有好处,让 PHP 清楚地知 道什么时候没有对象的引用,把空出的内存分配到其它用途。 第三节 定义一个类 当你声明一个类,你需要列出对象应有的所有变量和所有函数被称为 属性和方法. 3.1.1 中显示了一个类的构成. 注意在大括号()内你只能声 明变量或者函数. 3.1.2 中显示了如何在一个类中定义三个属性和两个方法. 3.1.1 3.1.2 当你声明属性,你不需要指明数据类型. 变量可能是整型,字符串或者是 另一个对象,这取决于实际情况.在声明属性时增加注释是一个好主意,标记 上属性的含义和数据类型. 当你声明一个方法,你所做的和在类外部定义一个函数是一样的. 方法 和属性都有各自的命名空间. 这意味着你可以安全地建立一个与类外部函数 同名的方法,两者不会冲突. 例如,一个类中可以定义一个名为 date()的方 法. 但是你不能将一个方法命名为 PHP 的关键字,如 for 或者 while. 类方法可能包含 PHP 中所谓的 type hint. Type hint 是另一个传递参 数给方法的类的名字. 如果你的脚本调用方法并传递一个不是类的实例的变 量,PHP 将产生一个”致命(fatal)错误” . 你可能没有给其它类型给出 type hint,就像整型,字符串,或者布尔值. 在书写的时候, type hint 是否 应当包含数组类型仍存在争议. Type hint 是测试函数参数或者运算符的实例的数据类型的捷径. 你可 能总是返回这个方法. 确认你强制让一个参数必须是哪种数据类型,如整型. 3.2.1 确保编译类只产生 Widget 的实例. 3.2.1 除了传递参数的变量外,方法含有一个特殊的变量. 它代表类的个别实例. 你应当用这个来指向对象的属性和其它方法.一些面向对象的语言假设一个 不合格的变量提交给本地属性,但在 PHP 中方法的任何变量只是在方法的一 定范围内. 注意在 User 类的构造函数中这个变量的使用(3.1.2). PHP 在属性和方法声明前定义一个访问限定语,如 public,private 和 protected. 另外,你可以用”static”来标记一个成员. 你也可以在类中声 明常量. 本章稍后会有不同访问方式的相关讨论. 你可以在一行中列出相同访问方式的几个属性,用逗号来分隔它们. 在 3.1.2 中,User 类有两个 private 属性-$password 和$lastLogin。 第四节 构造函数和析构函数 如果你在一个类中声明一个函数,命名为_construct,这个函数将 被当成是一个构造函数并在建立一个对象实例时被执行. 清楚地说,_是两 个下划线. 就像其它任何函数一样,构造函数可能有参数或者默认值. 你可 以定义一个类来建立一个对象并将其属性全放在一个语句(statement)中. 你也可以定义一个名为_destruct 的函数,PHP 将在对象被销毁前调用 这个函数. 它称为析构函数. 继承是类的一个强大功能. 一个类(子类/派生类)可以继承另一类(父类 /基类)的功能. 派生类将包含有基类的所有属性和方法,并可以在派生类中 加上其他属性和方法. 你也可以覆写基类的方法和属性. 就像 3.1.2 中显示 的,你可以用 extends 关键字来继承一个类. 你可能想知道构造函数是如何被继承的. 当它们和其它方法一起被继承 时,他们不会在创建对象时被执行. 如果你需要这个功能,你需要用第二章提到的:运算符. 它允许你指向一块 命名空间. parent 指向父类命名空间,你可以用 parent:_construct 来调 用父类的构造函数. 一些面向对象语言在类之后命名构造函数. PHP 的前几个版本也是如此,到 现在这种方法仍然有效.也就是:如果你把一个类命名为 Animal 并且在其中 建立一个命名也是 Animal 的方法,则这个方法就是构造函数.如果一个类的 同时拥有_construt 构造函数和与类名相同的函数,PHP 将把_construct 看作构造函数.这使得用以前的 PHP 版本所写的类仍然可以使用. 但新的脚 本(PHP5)应当使用_construct. PHP 的这种新的声明构造函数的方法可以使构造函数有一个独一无二的 名称,无论它所在的类的名称是什么. 这样你在改变类的名称时,就不需要改 变构造函数的名称. 你可能在 PHP 中给构造函数一个像其它类方法一样的访问方式. 访问方 式将会影响从一定范围内实例化对象的能力. 这允许实现一些固定的设计模 式,如 Singleton 模式. 析构函数,相反于构造函数. PHP 调用它们来将一个对象从内存中销毁. 默认地,PHP 仅仅释放对象属性所占用的内存并销毁对象相关的资源. 析构 函数允许你在使用一个对象之后执行任意代码来清除内存 当 PHP 决定你的脚本不再与对象相关时,析构函数将被调用. 在一个函 数的命名空间内,这会发生在函数 return 的时候. 对于全局变量,这发生于 脚本结束的时候. 如果你想明确地销毁一个对象,你可以给指向该对象的变 量分配任何其它值. 通常将变量赋值勤为 NULL 或者调用 unset . 下面的例子中,计算从类中实例化的对象的个数. Counter 类从构造函 数开始增值,在析构函数减值. 一旦你定义了一个类,你可以用 new 来建立一个这个类的实例. 类的定 义是设计图,实例则是放在装配线上的元件. New 需要类的名称,并返回该类 的一个实例. 如果构造函数需要参数,你应当在 new 后输入参数. 当你新建了一个实例,内存会被准备来存储所有属性. 每个实例有自己 独有的一组属性. 但方法是由该类的所有实例共享的。 第五节 克隆 PHP5 中的对象模型通过引用来调用对象, 但有时你可能想建立一个对 象的副本,并希望原来的对象的改变不影响到副本 . 为了这样的目的,PHP 定义了一个特殊的方法,称为_clone. 像_construct 和_destruct 一样, 前面有两个下划线. 默认地,用_clone 方法将建立一个与原对象拥有相同属性和方法的对 象. 如果你想在克隆时改变默认的内容,你要在_clone 中覆写(属性或方法). 克隆的方法可以没有参数,但它同时包含 this 和 that 指针(that 指向 被复制的对象). 如果你选择克隆自己,你要小心复制任何你要你的对象包含 的信息,从 that 到 this. 如果你用_clone 来复制. PHP 不会执行任何隐性 的复制, 下面显示了一个用系列序数来自动化对象的例子: 第六节 访问属性和方法 一个对象实例的属性是变量,就像 PHP 的其他变量一样. 但是你必须使 用-运算符来引用它们. 不需要在属性前使用美元符$. 例如, 6.1 中打印 User 对象的 name 属性那一行. 可以联用-,如果一个对象的属性包含了一个对象,你可以使用两个-运 算符来得到内部对象的属性. 你甚至可以用双重引用的字符串来放置这些表 达式. 看 6.5 中的例子,对象 House 中的属性 room 包含了一组 Room 对象. 访问方法和访问属性类似. -运算符用来指向实例的方法. 在例子 6.1 中 调用 getLastLogin 就是. 方法执行起来和类外的函数几乎相同. 如果一个类从另一类中继承而来,父类中的属性和方法将在子类中都有 效,即使在子类中没有声明. 像以前提到过的,继承是非常强大的. 如果你想访 问一个继承的属性,你只需要像访问基类自己的属性那样引用即可,使用:运 算符. PHP 有两个特殊的命名空间:parent 命名空间指向父类,self 命名空间 指向当前的类. 例子 6.6 中显示了如何用 parent 命名空间来调用父类中的构 造函数. 同时也用 self 来在构造函数中调用另一个类方法. 第四节中介绍了如何调用函数. 对于对象的成员来是这样调用的:如果 你需要在运行时确定变量的名称,你可以用$this-$Property 这样的表达式 . 如果你想调用方法,可以用$obj-$method(). 你也可以用-运算符来返回一个函数的值,这在 PHP 以前的版本中是不 允许的. 例如,你可以写一个像这样的表达式: $obj-getObject()-callMethod(). 这样避免了使用一个中间变量,也有助于实现某些设计模式,如 Factory 模式。 第七节 类的静态成员 类的静态成员与一般的类成员不同: 静态成员与对象的实例无关,只 与类本身有关. 他们用来实现类要封装的功能和数据,但不包括特定对象的功 能和数据. 静态成员包括静态方法和静态属性. 静态属性包含在类中要封装的数据,可以由所有类的实例共享. 实际上, 除了属于一个固定的类并限制访问方式外,类的静态属性非常类似于函数的 全局变量 我们在下例中使用了一个静态属性 Counter:$count. 它属于 Counter 类, 而不属于任何 Counter 的实例.你不能用 this 来引用它,但可以用 self 或其它 有效的命名表达. 在例子中,getCount 方法返回 self:$count,而不是 Counter:$count. 静态方法则实现类需要封装的功能,与特定的对象无关. 静态方法非常类 似于全局函数. 静态方法可以完全访问类的属性,也可以由对象的实例来访问,不 论访问的限定语是否是什么. 在 6.3 例中,getCount 是一个普通的方法,用- 来调用. PHP 建立一个 this 变量,尽管方法没有使用到.但是,getCount 不属于任何对象.在有些情况下,我 们甚至希望在不存在有效的对象时调用它,那么就应该使用静态方法. PHP 将 不在静态方法内部建立 this 变量,即使你从一个对象中调用它们 . 例子 6.7 由 6.3 改变 getCount 为静态方法而来. Static 关键字不能阻止一 个实例用- 运算符来调用 getCount,但 PHP 将不在方法内部建立 this 变量.如 果你使用 this-来调用 ,将会出错. /6.3 例指第四节-构造函数和析构函数中的例子( 参看前文),通过两个例 子的比较,你可以很好掌握 /static 方法与普通方法之间的区别. 你可以写一个方法通过判断 this 是否建立来显示是否它被静态地或者非 静态地调用. 当然,如果你用了 static 关键字,不管它怎样被调用,这个方法总 是静态的. 你的类也可以定义常量属性,不需要使用 public static,只需要用 const 关 键字即可. 常量属性总是静态的.它们是类的属性,而不是实例化该类的对象 的属性. Listing 6.7 Static members 第八节 访问方式 PHP5 的访问方式允许限制对类成员的访问. 这是在 PHP5 中新增的功 能,但在许多面向对象语言中都早已存在. 有了访问方式,才能开发一个可靠 的面向对象应用程序,并且构建可重用的面向对象类库. 像 C+和 Java 一样,PHP 有三种访问方式:public,private 和 protected. 对 于一个类成员的访问方式,可以是其中之一. 如果你没有指明访问方式,默认 地访问方式为 public. 你也可以为静态成员指明一种访问方式,将访问方式放 在 static 关键字之前(如 public static). Public 成员可以被毫无限制地访问.类外部的任何代码都可以读写 public 属性. 你可以从脚本的任何地方调用一个 public 方法. 在 PHP 的前几个版本 中,所有方法和属性都是 public, 这让人觉得对象就像是结构精巧的数组. Private(私有 )成员只在类的内部可见. 你不能在一个 private 属性所在的 类方法之外改变或读取它的值. 同样地,只有在同一个类中的方法可以调用一 个 private 方法. 继承的子类也不能访问父类中的 private 成员. 要注意,类中的任何成员和类的实例都可以访问 private 成员. 看例子 6.8,equals 方法将两个 widget 进行比较.=运算符比较同一个类的两个对象, 但这个例子中每个对象实例都有唯一的 ID.equals 方法只比较 name 和 price. 注意 equals 方法如何访问另一个 Widget 实例的 private 属性. Java 和 C 都允 许这样的操作. Listing 6.8 Private members 一个子类可能改变通过覆写父类方法来改变方法的访问方式,尽管如此, 仍然有一些限制. 如果你覆写了一个 public 类成员,他子类中必须保持 public. 如果你覆写了一个 protected 成员,它可保持 protected 或变成 public.Private 成 员仍然只在当前类中可见. 声明一个与父类的 private 成员同名的成员将简 单地在当前类中建立一个与原来不同的成员. 因此,在技术上你不能覆写一个 private 成员. Final 关键字是限制访问成员方法的另一个方法. 子类不能覆写父类中 标识为 final 的方法. Final 关键字不能用于属性. /haohappy 注:PHP5 的面向对象模型仍然不够完善,如 final 不像 Java 中那 样对 Data,Method 甚至 Class 都可以用。 第九节 绑定 除了限制访问,访问方式也决定哪个方法将被子类调用或哪个属性将被 子类访问. 函数调用与函数本身的关联,以及成员访问与变量内存地址间的关 系,称为绑定. 在计算机语言中有两种主要的绑定方式 静态绑定和动态绑定. 静态绑 定发生于数据结构和数据结构间,程序执行之前. 静态绑定发生于编译期, 因 此不能利用任何运行期的信息. 它针对函数调用与函数的主体,或变量与内存 中的区块. 因为 PHP 是一种动态语言 ,它不使用静态绑定. 但是可以模拟静 态绑定. 动态绑定则针对运行期产生的访问请求,只用到运行期的可用信息. 在面 向对象的代码中,动态绑定意味着决定哪个方法被调用或哪个属性被访问,将 基于这个类本身而不基于访问范围. Public 和 protected 成员的动作类似于 PHP 的前几个版本中函数的动作 , 使用动态绑定. 这意味着如果一个方法访问一个在子类中被覆写的类成员,并 是一个子类的实例,子类的成员将被访问(而不是访问父类中的成员). 看例子 6.10. 这段代码输出” Hey! I am Son.” 因为当 PHP 调用 getSalutation, 是一个 Son 的实例, 是将 Father 中的 salutation 覆写而来. 如果 salutation 是 public 的,PHP 将产生相同的结果. 覆写方法的操作很类似.在 Son 中,对于 identify 的调用绑定到那个方法. 即使在子类中访问方式被从 protected 削弱成 public, 动态绑定仍然会发 生. 按照访问方式使用的原则,增强对于类成员的访问限制是不可能的. 所以 把访问方式从 public 改变成 protected 不可能进行. Listing 6.10 Dynamic binding 动态绑定 /注 : 在子类中没有覆写 getSalutation(),但实际上仍然存在一个 getSalutation().这 个类中的$salutation 和 identify() /与 Son 子类的实例中的 getSalutation()方法动态绑定,所以调用 Son 的实例 的 getSalutation()方法, /将调用 Son 类中的成员 salutation 及 identify(),而不是父类中的成员 salutation 及 identify(). Private 成员只存在于它们所在的类内部. 不像 public 和 protected 成员 那样,PHP 模拟静态绑定 . 看例子 6.11. 它输出”Hello there! I am Father.”,尽管 子类覆写了 salutation 的值. 脚本将 this-salutation 和当前类 Father 绑定. 类 似的原则应用于 private 方法 identify(). Listing 6.11 Binding and private members 动态绑定的好处是允许继承类来改变父类的行为,同时可以保持父类的 接口和功能. 看例子 6.12. 由于使用了动态绑定,在 deleteUser 中被调用的 isAuthorized 的 version 可以由对象的类型来确定. 如果是一个普通的 user,PHP 调用 User:isAuthorized 会返回 FALSE.如果是一个 AuthorizedUser 的实例,PHP 调用 AuthorizedUser:isAuthorized,将允许 deleteUser 顺利执行 . /haohappy 注 :用一句话说清楚 ,就是对象类型与方法,属性绑定. 调用一 个父类与子类中都存在的方法或访问一个属性时,会先判断实例属于哪种对 象类型,再调用相应的类中的方法和属性. Listing 6.12 动态绑定的好处 为什么 private 的类成员模拟静态绑定? 为了回答这个问题, 你需要回忆 一下为什么需要有 private 成员.什么时候用它们来代替 protected 成员是有意 义的? private 成员只有当你不想让子类继承改变或特殊化父类的行为时才用 到. 这种情况比你想像的要少. 通常来说,一个好的对象分层结构应当允许绝 大多数功能被子类特殊化,改进,或改变 这是面向对象编程的基础之一. 一 定的情况下需要 private 方法或变量,例如当你确信你不想允许子类改变父类 中的某个特定的部份. 第十节 抽象方法和抽象类 面向对象程序通过类的分层结构构建起来. 在单重继承语言如 PHP 中, 类的继承是树状的. 一个根类有一个或更多的子类,再从每个子类继承出一个 或更多下一级子类. 当然,可能存在多个根类,用来实现不同的功能. 在一个良 好设计的体系中,每个根类都应该有一个有用的接口, 可以被应用代码所使用. 如果我们的应用代码被设计成与根类一起工作,那么它也可以和任何一个从 根类继承出来的子类合作. 抽象方法是就像子类中一般的方法的占位符(占个地方但不起作用), 它与 一般方法不同 没有任何代码. 如果类中存在一个或更多抽象方法, 那么这 个类就成了抽象类. 你不能实例化抽象类. 你必须继承它们,然后实例化子类. 你也可以把抽象类看成是子类的一个模板. 如果你覆写所有的抽象方法, 子类就变成一个普通的类. 如果没有覆写 所有方法, 子类仍是抽象的. 如果一个类中中包含有抽象方法(哪怕只有一个), 你必须声明这个类是抽象的, 在 class 关键字前加上 abstract. 声明抽象方法的语法与声明一般方法不同. 抽象方法的没有像一般方法 那样包含在大括号中的主体部份,并用分号;来结束. 在例子 6.13 中, 我们定义了一个含有 getArea 方法的类 Shape. 但由于 不知道形状不可能确定图形的面积,确良我们声明了 getArea 方法为抽象方法 . 你不能实例化一个 Shape 对象,但你可以继承它或在一个表达式中使用它, 就 像例 6.13 中那样. 如果你建立了一个只有抽象方法的类,你就定义了一个接口(interface). 为了说明这种情况, PHP 中有 interface 和 implements 关键字. 你可以用 interface 来代替抽象类, 用 implements 来代替 extends 来说明你的类定义或 使用一个接口. 例如, 你可以写一个 myClass implements myIterface. 这两种 方法可以依个人偏爱来选择. /*注: 两种方法即指: 1. abstract class aaa (注意 aaa 中只有抽象方法,没有一般方法) class bbb extends aaa (在 bbb 中覆写 aaa 中的抽象方法) 2. interface aaa class bbb implements aaa (在 bbb 中覆写 aaa 中的抽象方法) */ Listing 6.13 Abstract classes 第十一节 重载 PHP4 中已经有了重载的语法来建立对于外部对象模型的映射,就像 Java 和 COM 那样. PHP5 带来了强大的面向对象重载,允许程序员建立自定义的 行为来访问属性和调用方法. 重载可以通过_get, _set, and _call 几个特殊方法来进行. 当 Zend 引 擎试图访问一个成员并没有找到时,PHP 将会调用这些方法. 在例 6.14 中,_get 和_set 代替所有对属性变量数组的访问. 如果必要 , 你可以实现任何类型你想要的过滤. 例如,脚本可以禁止设置属性值, 在开始 时用一定的前缀或包含一定类型的值. _call 方法说明了你如何调用未经定义的方法. 你调用未定义方法时,方 法名和方法接收的参数将会传给_call 方法, PHP 传递_call 的值返回给未定 义的方法. Listing 6.14 User-level overloading 类的自动加载 当你尝试使用一个未定义的类时,PHP 会报告一个致命错误. 解决方法 就是添加一个类,可以用 include 包含一个文件 . 毕竟你知道要用到哪个类. 但是,PHP 提供了类的自动加载功能, 这可以节省编程的时间. 当你尝试使用 一个 PHP 没有组织到的类 , 它会寻找一个 _autoload 的全局函数. 如果存在 这个函数,PHP 会用一个参数来调用它,参数即类的名称. 例子 6.15 说明了_autoload 是如何使用的 . 它假设当前目录下每个文件 对应一个类. 当脚本尝试来产生一个类 User 的实例,PHP 会执行_autoload. 脚本假设 class_User.php 中定义有 User 类 不管调用时是大写还是小写, PHP 将返回名称的小写. Listing 6.15 Class autoloading 第十三节 对象串行化 串行化可以把变量包括对象,转化成连续 bytes 数据. 你可以将串行化后 的变量存在一个文件里或在网络上传输. 然后再反串行化还原为原来的数据. 你在反串行化类的对象之前定义的类,PHP 可以成功地存储其对象的属性和 方法. 有时你可能需要一个对象在反串行化后立即执行. 为了这样的目的, PHP 会自动寻找 _sleep 和_wakeup 方法. 当一个对象被串行化,PHP 会调用 _sleep 方法(如果存在的话). 在反串 行化一个对象后,PHP 会调用_wakeup 方法. 这两个方法都不接受参数. _sleep 方法必须返回一个数组 ,包含需要串行化的属性. PHP 会抛弃其它属 性的值. 如果没有_sleep 方法,PHP 将保存所有属性. 例子 6.16 显示了如何用_sleep 和_wakeup 方法来串行化一个对象 . Id 属性是一个不打算保留在对象中的临时属性. _sleep 方法保证在串行化的对 象中不包含 id 属性. 当反串行化一个 User 对象,_wakeup 方法建立 id 属性 的新值. 这个例子被设计成自我保持. 在实际开发中,你可能发现包含资源 (如图像或数据流)的对象需要这些方法. Listing 6.16 Object serialization 第十四节 命名空间 命名变量,函数和类是挺困难的,除了要考虑到变量的名称要易于理解,还 要担心是否这个名称在其它某个地方已经使用过. 在一小段脚本中,第二个问 题是基本问题. 当你考虑重用你的代码, 在这之后的项目代码必须避免使用 你用过的命名. 通常来说,可重用的代码总是包含在函数或类中, 需要处理许 多可能发生的命名冲突. 但函数和类之间也可能发生命名冲突. 你可以尝试 避免出现这种情况,通过在所有类前添加前缀的方法,或者你可以使用 namespace 语句. Namespace 关键字给一块代码命名. 在这个代码块外部,脚本必须用操作 符: 加上命名空间的名称来引用这个代码块. 引用静态的类成员也是用相同 的方法. 在命名空间内代码不需要声明命名空间, 它本身就是默认的. 这种 方法比添加前缀的方法好. 你的代码可由此变得更加紧凑和可读. 你可能想知道是否可以建立分层的(嵌套的) 命名空间. 答案是不可以 . 但你可以在命名空间名称后加上冒号, 你可以再次调用在名称中不包含冒号 的变量,函数和类. 命名空间允许存在冒号,只要不是第一个字符和最后一个 字符或接着另一个冒号. 命名空间的名称中的冒号对于 PHP 来说没有任何 意义, 但如果你用他们来区分逻辑上的区块, 他们可以很好地说明你的代码 中的父子(parent-child)关系. /* 注: 即可以使用这样: namespace animal:dog namespace animal:pig 用冒号来说明 parent-child 关系. */ 你可能在一个命名空间语句内没有包含函数,类或常量定义以外的任何 东西. 这将阻止你使用他们来改进旧的使用全局变量的函数库. 命名空间最 适合于面向对象. 命名空间内的常量与类中的常量使用相同的语法. 例子 6.17 显示了如何使用命名空间. Listing 6.17 Using a namespace Import 语句把命名空间中的某个部份导入全局的命名空间. 要导入单一的命名空间的成员,可以指定类型为 constant,function 或 class,接 着写上成员的名称; /如 import class XXX 如果你想导入某一特定类型的所有成员,你可以用*来代替名称; /如 import constant * 导入所有常量 如果你想导入所有类型的所有成员,用*即可. /如 import * 在成员之后,用 from 关键字加上命名空间的名称. /如 import class textEngine from core_php:utility; 总之你要写成像 import * from myNamespace 或 import class textEngine from core_php:utility 这样的语句,就像例 6.17 中那样. 第十五节 Zend 引擎的发展 本章的最后一节,Zeev 讨论了 Zend 引擎带来的对象模型,特别提到它与 PHP 的前几个版本中的模型有什么不同. 当 1997 年夏天,我们开发出 PHP3, 我们没有计划要使 PHP 具备面向对 象的能力. 当时没有任何与类和对象有关的想法. PHP3 是一个纯粹面向过程 的语言. 但是,在 1997.8.27 的晚上 PHP3 alpha 版中增加了对类的支持. 增加 一个新特性给 PHP,当时仅需要极少的讨论 ,因为当时探索 PHP 的人太少 . 于 是从 1997 年八月起, PHP 迈出了走向面向对象编程语言的第一步 . 确实,这只是第一步. 因为在这个设计中只有极少的相关的想法,对于对 象的支持不够强大. 这个版本中使用对象仅是访问数组的一个很酷的方法而 已. 取代使用$foo“bar”,你可以使用看起来更漂亮的$foo-bar. 面向对象方 法的主要的优势是通过成员函数或方法来储存功能. 例子 6.18 中显示了一个 典型的代码块. 但是它和例 6.19 中的做法其实并没有太大不同. Listing 6.18 PHP 3 object-oriented programming PHP3 中的面向对象编程 Listing 6.19 PHP 3 structural programming PHP3 PHP3 中的结构化编程 以上我们在类中写上两行代码,或者显示地传递数组给函数. 但考虑到 PHP3 中这两种选择并没有任何不同,我们仍然可以仅把对象模型当成一种” 语法上的粉饰”来访问数组 . 想要用 PHP 来进行面向对象开发的人们 ,特别是想使用设计模式的人,很 快就发现他们碰壁了. 幸运地,当时(PHP3 时代)没有太多人想用 PHP 来进行 面向对象开发. PHP4 改变了这种情况 . 新的版本带来了引用 (reference)的概念 , 它允许 PHP 的不同标识符指向内存中的同一个地址. 这意味着你可以使用两个或更 多的名称来给同一个变量命名,就像例 6.20 那样. Listing 6.20 PHP 4 references PHP4 中的引用 由于构建一个指向彼此的对象网络是所有面向对象设计模式的基础,这 个改进具有非常重大的意义.当引用允许建立更多强大的面向对象应用程序, PHP 对待对象和其它类型数据相同的做法带给开发者极大的痛苦.就像任何 PHP4 的程序员将会告诉你的, 应用程序将会遭遇 WTMA(Way Too Many Ampersands 过多 一个返回 this- value 的值 ;另一个返回 this-value-me 的值. 但是-$this 不是相同的东西吗 ? MyFoo:getValue()和 MyFoo:getValueFromMe()返回的值不是一样的吗? 首先,我们调用 CreateObject(“foo“),这会返回一个 MyFoo 类型的对象 . 然后我们调用 MyFoo:setValue(7). 最后,我们调用 MyFoo:getValue() 和 MyFoo:getValueFromMe(), 期望得到返回值 7. 当然,如果我们在任何情况下都得到 7, 以上这个例子将不是本书中最没 有意义的例子. 所以我相信你已经猜到我们得不到两个 7 这样的结果. 但是我们将得到什么结果,并且更重要地,为什么呢? 我们将得到的结果分别是 7 和 5. 至于为什么-有三个很好的理由 . 首先,看构造函数. 当在构造函

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论