面向对象编程概述.doc_第1页
面向对象编程概述.doc_第2页
面向对象编程概述.doc_第3页
面向对象编程概述.doc_第4页
面向对象编程概述.doc_第5页
已阅读5页,还剩23页未读 继续免费阅读

下载本文档

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

文档简介

第1章 面向对象编程概述第 章面向对象编程概述面向对象(Object-Oriented,OO)软件开发对于主要编写过程式代码的开发人员而言是一个非常令人困惑的主题。但是,情况本不该变成如此。本章将讨论OO背后的一些基本理论,并介绍OO的一些难以理解的术语(有一些是令人望而生畏的术语)。同时,您还将认识到为什么您会对OO技术感兴趣,以及OO技术如何极大地提高开发复杂应用程序的速度。并且,您还可以看到修改这些应用程序是非常简单的工作。接下来的几章将扩展在本章中给出的一些基本思想,并帮助您熟悉一些更高级的主题。如果您已经在PHP 6以外学习过OO开发,那么可以跳过本章和下一章。但是,这些内容很好地总结了面向对象编程的基本原理,因此建议完整阅读这些内容。1.1 面向对象编程的概念面向对象编程(Object-Oriented Programming,OOP)需要以一种不同的方式来考虑如何构造应用程序。通过对象可以在对应用程序所处理的现实任务、过程和思想进行编码时,实施更贴切的建模。OOP方法并不是将应用程序考虑成一个将大量数据从一个函数传递给下一个函数的控制线程,而是允许将应用程序建模成一组相互协作的对象,并且这些对象可以独立地处理某些活动。可以做如下类比:在造一幢房屋时,管道工程师主要处理管道,而电气工程师则主要负责处理电线。管道工程师并不需要知道卧室中的电路是10安培还是20安培,他们需要关注的仅仅是自己的活动。总承包人保证每个子承包人都能够完成他们需要完成的工作,但是他不一定要关注每个任务的具体细节。OOP方法与此类似,其中每个对象均对其他的对象隐藏其实现细节。每个对象如何完成其任务与系统中的其他组件并不相关。对象之间的关联就是对象所能够提供的服务。类和对象的概念,以及在软件开发过程如何运用这些概念,正是隐藏在OOP背后的基本思想。从某种意义上说,OOP是和过程编程相对立的,过程编程使用函数和全局数据结构来实施编程。OOP方法具有好于过程编程的优点以及(PHP对OO支持的全新实现首次出现在PHP 5中,并在PHP 6中做了进一步的改进)巨大的性能改进。1.1.1 OOP的优点OOP的主要优点之一就是可以轻松地将单个的业务需求转换为单个的代码模块。由于OOP方法允许基于现实世界对象的思想来对应用程序进行建模,因此通常可以识别人、事物以及概念和等价类之间的直接关联。这些类具有与它们所表示的现实世界概念相同的属性和行为,这将有助于快速确定必须要编写哪些代码,以及应用程序的不同部分之间必须如何实施交互。OOP的第二个优点就是代码重用。通常,在同一个应用程序的不同位置需要相同类型的数据。例如,一个帮助医院管理其病人记录的应用程序肯定需要一个名为Person的类。在病人护理中涉及到大量人员 病人、医生、护士、医院管理人员、保险理赔人员等。在病人护理的每个步骤中,该病人的记录中需要说明哪个人正在执行给定的操作(例如开处方、清理伤口或邮寄账单给保险公司),并且验证此人获准执行该操作。通过定义一个包含了所有人员共用的属性和方法的通用类Person,就可以实现大量的代码重用,而代码重用在过程编程方法中并不总是能够实现。其他的应用程序又如何呢?如果在某个时刻让您想象一下,您会想到有多少应用程序涉及到个人信息的处理呢?可能会有很多。一个编写良好的Person类可以方便地从一个项目复制到另一个项目,而无需做多大的改动,甚至是不需要做任何改动。这样,代码重用立即就可以使您拥有之前所开发的处理人员信息所需要的所有丰富功能。这是OO方法最大的优点之一:在给定应用程序中重用代码,也可以跨不同项目重用代码。OOP的另外一个优点源于类的模块化。如果在Person类中发现了一个错误,或者希望往类中添加一些功能或改变类的工作方式,您只需要在一个位置进行处理。该类的所有功能均包含在一个单独的文件中。依赖于Person类的应用程序的所有进程都将立即被该类的改动所影响。这可以极大地简化错误的检查过程,并使得添加功能成为一个相对简单的任务。1.1.2 一个现实世界的示例在一个较小的应用程序中,上面提到的优点看上去似乎有点微不足道;但是在一个比较复杂的软件体系结构中,模块化的优点可能就非常显著。本书的作者之一曾经致力于完成一个具有200 000多行过程式PHP代码的项目。毫无疑问,花费在修改漏洞上的时间中的65%都用在寻找特定函数的位置,以及确定哪些数据和哪些函数交互。随后在OO体系结构中对该软件的重写导致代码量大大降低。如果一开始就以这种方式实现该应用程序,那么不仅是从一开始就只需花费较少的开发时间,而且可以使代码中的漏洞也比较少(代码量越少,出问题的机会也就越少),同时花费在漏洞修补上的开发周转时间也将大幅减少。由于OO方法本身就是一个非常适合于清晰描述应用程序的结构的系统(参见第2章),因此当您还是开发团队的一个新手时,了解一个已有应用程序的结构将更加方便。此外,OOP还提供了一个架构,用于帮助您确定可能想开发的新功能的合适位置。比较大的项目通常都会有一个包含多个成员的软件开发团队,而这个团队通常都包含具有不同能力的程序员。在这种情况下,OO方法与过程式方法相比也具有明显的优势。对象向它们的用户隐藏了其实现细节。这些团队的低级成员并不需要理解复杂的数据结构以及所有业务逻辑的内涵,就可以开始使用这些由比较有经验的成员所创建的对象(所需要的仅仅是一些文档)。这些对象自己负责触发数据的改动或者是系统状态的改动。当本章前面所提到的大型应用程序仍然使用过程式代码编写时,软件开发团队的新成员通常需要花费两个月的时间来学习该应用程序。在使用对象重新构建该软件以后,团队的新成员通常仅仅需要花费几天时间就可以开始在代码库中添加实质性的内容。新成员甚至可以快速地使用最复杂的对象,因为他们并不需要完全理解包含在这些对象中的功能块如何实现的细节。既然已经对为什么应该考虑使用OO范例作为编程方法有了很好的了解,那么应该阅读下面的几节以更好地理解隐藏在OO背后的基本概念。如果一切都正常进行,那么通过后续两章的学习,您将开始认识到OOP方法的优势。1.2 理解OOP概念本节介绍面向对象编程的主要概念,并探索这些概念之间如何交互;第3章将深入讨论这些概念在PHP6中的实现细节。本章涵盖了以下主题: 类(Class) 一个对象的“蓝本”,包含了定义属性和方法的实际代码; 对象(Object) 某个类的运行实例,包含应用程序运行所需要的所有内部数据和状态信息; 继承(Inheritance) 可以定义一种类型类为不同类型类的子类型(类似于正方形是一种矩形)的能力; 多态(Polymorphism) 允许一个类定义为多个类范畴的成员(例如,轿车是“有引擎的事物”以及“有轮子的事物”); 接口(Interface) 是指这样一种做法:设定对象能够做什么,而不具体定义这些功能(如,狗和人都是“可以行走的事物”,但是行走的方式有很大的不同); 封装(Encapsulation) 对象就其内部数据的访问进行保护的能力。如果这些术语中有哪个难以理解,不必担心,本章后面的内容将会使所有概念变得清晰。新学习的知识可能会完全改变您在实施软件开发项目时所选用的方法。1.3 类在现实世界中,对象均具有一些特征和行为。轿车具有相应的颜色、重量、制造厂商和一定容量的油缸,这些都是它的特征。轿车可以加速、停车、发出转弯信号以及鸣笛,所有这些就是它的行为。这些特征和行为对于所有的轿车都是通用的。尽管在某个停车场中两个特定的轿车可能具有不同的颜色,但是所有的轿车都有颜色。通过使用一个称为类的构造,OOP允许您将轿车理解成具有所有这些特征的某种事物。类是一个代码单元(由变量和函数组成),该代码单元描述了集合中所有成员的特征和行为。一个名为Car的类将描述所有轿车都通用的属性和方法。在OO术语中,类的特征称为它的属性。属性具有名称和值。某些属性允许对其值进行更改;而另外一些则不允许进行这样的改动。例如,在Car类中,可能会有诸如color和weight之类的属性。尽管通过新的喷漆工作可以改变轿车的颜色,但是轿车的皮重(不包括货物和乘客)将是一个固定值。某些属性表示对象的状态。状态指的是对象的某些特征,这些特征将由于某些事件的发生而改变,而不一定是对象自身直接对其进行更改。在模拟车辆性能的应用程序中,Car类可能具有名为velocity的属性。轿车的速度是一个不能由自己改变的值,而是由传送给引擎的燃料量、引擎的性能特征以及轿车正在其中行驶的地貌等多个因素确定。类的行为称为它的方法。类的方法在语法上等价于在传统过程式代码中的函数。与函数一样,方法可以接受任意多个参数,各个参数具有任意有效的数据类型。某些方法处理作为参数传入的外部数据,但是也可以处理对象的属性,要么使用这些属性来通知由方法执行的操作(例如,当名为accelerate的方法检查剩余的燃料量以确定轿车是否能够加速时),要么通过改变诸如轿车速度之类的值来改变对象的状态。1.3.1 对象首先,类可以看成是构造对象的蓝本。如同很多房屋都是基于相同的蓝图构建一样,也可以根据某个类创建一个对象的多个实例;但是蓝本并没有设定相应的细节,如墙的颜色、地板类型等,它仅仅设定了这些要素将存在。类的工作原理与此非常类似。类设定了对象将拥有的行为和特征,但是不一定设定这些特征的值。对象是利用类所提供的蓝本构造的一个具体实体。如果将一所房屋类比成一个类,那么您的房屋(房屋的一个特定实例)就相当于一个对象。有了相应的蓝本和一些建筑材料以后,就可以构造一所房屋。在OOP中,当使用类来构建一个对象时,该过程称为实例化。实例化一个对象需要两方面信息: 该对象将加载到其中的内存位置,由PHP自动处理; 将生成属性值的数据。这些数据可以来源于数据库、某个普通的文本文件、另外一个对象或其他的数据源。类永远不能有属性值或状态,只有对象才可以拥有属性值或状态。在为房屋贴上墙纸或刷上乙烯基涂料之前,必须使用蓝本来构建房屋。同样,在与对象的属性进行交互或调用其方法之前,必须根据类实例化对象。在设计中改变方法或属性时,我们操作的是类。而对象则是在运行时对其进行操作。在该阶段,将相应的值赋给对象的属性,并且调用对象的方法。何时应该使用“类”这个单词,何时应该使用“对象”这个单词,这个问题经常会使OOP编程的新手混淆。在实例化对象后,就可以将对象用于实现应用程序的业务需求。现在具体查看在PHP中如何实现这一点。1. 创建类首先从一个简单的示例开始,将下面的代码保存在名为class.Demo.php的文件中。这样就创建了Demo类。尽管这并不是什么非常令人兴奋的事情,但这是在PHP中声明一个新类的基本语法。使用关键字class以使PHP知道将定义一个新类,其后是类名和一对大括号,大括号用来指出该类的代码的开始和结束位置。注意:使用清晰定义的约定组织您的源代码文件非常重要。一个应该遵循的良好规则是将每个类都放在它自己的文件中,并将该文件命名为class.ClassName.php。可以实例化Demo类型的一个对象,如下所示:为了实例化一个对象,首先需要通过引入包含类的文件(在本例中为class.Demo.php文件)以保证PHP知道在何处查找类的声明;然后,调用new操作符并提供类的名称,后跟开始和结束圆括号。该语句的返回值被赋给一个新的变量,在本例中为objDemo。现在,可以调用$objDemo对象的方法,并检查或设置其属性的值 如果它确实有属性的话。尽管这里创建的类确实没有执行任何操作,但它仍然是一个有效的类定义。2. 添加方法如果Demo类不能执行任何操作,那么它就不是特别有用,因此接下来查看如何创建一个方法。记住,类的方法基本上只是一个函数。可以通过在类的大括号中编写一个函数来将方法添加到该类中。下面是一个示例:从该类派生的对象现在可以为调用sayHello方法的所有人打印一个问候。为了调用$objDemo对象的该方法,需要使用-操作符来访问新创建的函数:sayHello(Steve); ?该对象现在可以打印一个友好的问候。-操作符用于访问对象的所有方法和属性。注意:对于在其他的编程语言中研究过OOP的读者而言,可以注意到-操作符总是用于访问对象的方法和属性。PHP在其OO语法中根本不使用点操作符(.)。3. 添加属性向类中添加一个属性与添加一个方法一样容易,仅仅需要在类中声明一个变量以保存属性的值即可。在面向过程代码中,当希望存储某个值时,会将该值赋给一个变量。在OOP中,当希望存储某个属性的值时,也可以使用一个变量。该变量在类声明的顶部声明,类声明位于包含类代码的大括号中。变量的名称就是属性的名称。如果变量名为$color,那么它就有一个名为color的属性。打开class.Demo.php文件,并添加如下突出显示的代码:name!; ?要创建Demo类中名为name的属性,就只需声明新的变量$name即可。为了访问该属性,需要使用与前面示例中一样的-操作符以及属性名。改写后的sayHello方法展示了如何访问该属性的值。创建名为testdemo.php的新文件,并在其中添加如下的代码:name = Steve; $objAnotherDemo = new Demo(); $objAnotherDemo-name = Ed; $objDemo-sayHello(); $objAnotherDemo-sayHello(); ?保存该文件,然后在Web浏览器中打开该文件。字符串“Hello Steve!”和“Hello Ed!”将打印到屏幕上。关键字public用于让类知道允许从类外部访问该关键字后面的变量。类中某些成员变量的存在仅仅为了类自身使用它,而外部代码不可以访问它们;我们需要把这些变量声明为private或protected(稍后将详细介绍)。在本例中,我们希望能够设置和获取name属性的值。注意,sayHello方法的工作方式已经发生了改变。它并没有使用任何参数,而是从name属性中获取值。该代码使用了$this变量,因此该对象可以获取关于其自身的信息。例如,您可能已经拥有了某个类的多个对象,并且事先不知道对象变量的名称是什么,那么使用$this变量将可以引用当前实例。在前面的示例中,第一次调用sayHello打印了“Steve”,而第二次调用sayHello则打印了“Ed”。这是因为$this变量允许各个对象访问其自己的属性和方法,而不需要知道在外部应用程序中表示该对象的变量的名称。本章前面已经介绍过一些属性会影响某些方法的操作,例如Car类中的accelerate方法需要检查剩余的燃料量。在accelerate方法中的代码将使用诸如$this-amountOfFuel的代码来访问此燃料量属性。注意:在访问属性时,仅仅需要使用一个$,语法是$obj-property,而不是$obj-$property。这一点对于PHP的新手而言可能会产生一些困惑。属性变量声明为public $property,但是使用$obj-property来访问。除了用于存储类的属性值的变量外,另外有一些变量可能声明用于类的某些内部操作。这两种数据统称为类的内部成员变量。某些内部成员变量可以以属性的形式由类外部的代码访问,而另外一些内部成员变量则不能如此操作,而是严格地用于内部处理。例如,如果Car类由于某种原因需要从一个数据库中获取信息,那么它可能在某个内部成员变量中保存某个数据库连接句柄。该数据库连接句柄显然不是轿车的属性,但它确实是类执行某些操作所需要的事物。4. 保护对成员变量的访问正如前面的示例所示,可以将name属性的值设置为任意所需的数据 包括对象、整数数组、文件句柄或任何其他没有意义的值。但是,并不能在设置name属性时执行任何形式的数据验证或更新任何其他的值。为了解决该问题,必须以getproperty name和setproperty name的函数形式来实现属性。这些函数称为存取器方法,下面的示例演示了这些方法。将class.Demo.php修改成如下所示:getName() . ! public function getName() return $this-_name; public function setName($name) if(!is_string($name) | strlen($name) = 0) throw new Exception(Invalid name value!); $this-_name = $name; ?编辑testdemo.php,如下所示:setName(Steve);$objDemo-sayHello(); $objDemo-setName(37); /would trigger an error ?name属性的成员访问级别已经由public变成了private,并且增加了一个下划线作为前缀。下划线是建议的命名约定,表明私有的成员变量和函数;但它仅仅是一个约定而已 PHP并不要求这么做。关键字private使得对象外部的代码不能修改其值。私有的内部成员变量不能从类的外部访问。由于不能直接访问这些变量,因此必须使用getName()和setName()存取器方法获得这些信息,从而确保类可以在允许设置值之前检查该值。在这个示例中,如果将一个无效的值提供给了name属性,系统将抛出一个异常。此外,函数的public访问说明符也已经添加到类中。public是所有没有显式设置可见性级别的成员变量或函数的默认可见性级别,但是始终显式地声明类的所有成员的可见性是一种不错的习惯。成员变量或方法可以有3个不同级别的可见性:公有的、私有的和受保护的。公有成员可以由任意的代码访问。私有成员只有类自身可以访问。这些成员通常用于类的内部处理,并且可能包含诸如某个数据库连接句柄或配置信息之类的内容。受保护的成员可以由类自身以及从该类继承的类使用(我们将在本章稍后定义继承并展开详细讨论)。通过为所有属性创建存取器方法,可以简化添加数据验证或新的业务逻辑的工作,也可以简化在后面对对象执行其他的改动。尽管应用程序的当前业务需求在给定属性上并没有涉及数据验证,但是仍然应该使用get和set函数来实现该属性,这样就可以在将来添加验证或业务逻辑功能。注意:应该总是对属性使用存取器方法。这样,将来对业务逻辑和数据验证需求执行的改动都将更加容易实现。5. 初始化对象对于许多将要创建的类,需要在初次实例化该类的对象时执行一些特殊的设置。例如,可能需要从数据库中获取某些信息,或者是初始化某些属性值。通过创建一个称为构造函数的特殊方法可以执行实例化对象所需要的任何活动,该方法在PHP中是通过名为_construct()的函数来实现的。在实例化对象时,PHP将自动调用这个特殊的函数。例如,可以按照下面的方式重写Demo类:name = $name; function sayHello() print Hello $this-name!; ?_construct函数将在实例化类Demo的一个新对象时自动被调用。注意,需要更新testdemo.php文件以将变量name传递给构造函数,而不是传递给setter方法。注意:在PHP 4中,对象构造函数与类同名。PHP 5中改变了这一点,使用统一的构造函数方案。为了向后兼容,PHP首先查找名为_construct的函数,如果没有发现该函数,那么它仍然将查找一个与类同名的函数(在前面的示例中为public function Demo()。尽管这种向后兼容性在PHP 6中仍然得到了支持,但是并不能保证它在将来的版本中还会得到支持。如果有这样一个类,它不需要任何特殊的初始化代码就可以运行,那么就不需要创建构造函数。正如在Demo类的第一个版本中所看到的那样,PHP自动执行所需的操作以创建该对象。只有在需要构造函数时才创建该函数。6. 销毁对象当请求的页面已经完成运行,或者创建的对象变量已经不在其作用域内,又或者变量被显式地设置为null时,都需要从系统内存中移除该变量。在PHP 6中,可以在销毁对象之前做一些处理工作,并且在销毁发生时采取相应的操作。为了实现这一点,创建一个不带参数的、名为_destruct的函数。在销毁对象之前就会自动调用该函数,前提是该函数存在。调用该函数可以在销毁对象之前执行任何最后的清理工作(例如,关闭已经由该类打开的文件句柄或数据库连接),或者在销毁对象之前执行任何最后的内部处理工作。下面的示例从一个数据库中获取某个对象的属性。如果对象的任何属性改变,这些属性都将在销毁对象之前自动保存回数据库中,这样就不需要显式调用一个保存方法。该析构函数还关闭了打开的数据库连接句柄。与本书的大部分数据库示例一样,该示例使用了PostgreSQL作为其平台。我们坚信PostgreSQL提供的高级功能、事务支持以及健壮的存储过程机制,可以使其成为比MySQL以及其他面向大规模企业软件开发的开源关系数据库管理系统(Relational Database Management System,RDBMS)更好的选择。如果在配置中没有PostgreSQL环境,或者您更喜欢使用MySQL(很多人都是如此),那么可以根据使用的数据库平台进行相应的修改。使用如下的SQL语句创建一个名为widget的表:CREATE TABLE widget ( widgetid SERIAL PRIMARY KEY NOT NULL, name varchar(255) NOT NULL, description text);插入一些数据:INSERT INTO widget (name, description)VALUES(Foo, This is a footacular widget!);创建一个名为class.Widget.php的文件,并在该文件中输入以下的代码:hDB = pg_connect(dbname=parts user=postgres); if(! is_resource($this-hDB) throw new Exception(Unable to connect to the database.); $sql = SELECT name, description FROM widget WHERE widgetid = $widgetID; $rs = pg_query($this-hDB, $sql); if(! is_resource($rs) throw new Exception(An error occurred selecting from the database. ); if(! pg_num_rows($rs) throw new Exception(The specified widget does not exist!); $data = pg_fetch_array($rs); $this-id = $widgetID; $this-name = $dataname; $this-description = $datadescription; public function getName() return $this-name; public function getDescription() return $this-description; public function setName($name) $this-name = $name; $this-needsUpdating = true; public function setDescription($description) $this-description = $description; $this-needsUpdating = true; public function _destruct() if($this-needsUpdating) $sql = UPDATE widget SET ; $sql .= name = . pg_escape_string($this-name) . , ; $sql .= description = . pg_escape_string($this-description) . ; $sql .= WHERE widgetID = . $this-id; $rs = pg_query($this-hDB, $sql); /Were done with the database. Close the connection handle. pg_close($this-hDB); ?该对象的构造函数使用默认的超级用户账户postgres打开了一个到数据库parts的连接。该连接句柄保存在一个私有成员变量中以备将来使用。作为参数传递给构造函数的ID值将用于构造一条SQL语句,该语句提取数据库中具有指定主键的窗口部件(widget)的相关信息。然后,将来源于数据库的数据赋给私有成员变量以供get和set函数使用。注意,如何任何代码发生了错误,那么构造函数都将抛出异常,因此需要保证将所有尝试创建Widget对象的代码都包装在trycatch块中。两个存取器方法getName()和getDescription()可用来获取私有成员变量的值。同样,setName()和setDescription()方法可用来将新的值赋给这些变量。注意,当赋予一个新值时,应该把needsUpdating值设置为true。如果没有任何内容发生改动,那么就不需要执行更新。为了测试这一点,创建一个名为testWidget.php的文件,该文件包含以下内容:getName() . n; print Widget Description: . $objWidget-getDescription() . n; $objWidget-setName(Bar); $objWidget-setDescription(This is a bartacular widget!); catch (Exception $e) die(There was a problem: . $e-getMessage(); ?在Web浏览器中访问该文件。该文件第一次运行时,输出应该类似于如下:Widget Name: FooWidget Description: This is a footacular widget!而后续的调用将显示如下内容:Widget Name: BarWidget Description: This is a bartacular widget!接下来查看该技术的功能有多么强大。可以从数据库中获取一个对象,更改该对象的某个属性,然后自动将改动信息写回到数据库中,而完成所有这些工作只需要testWidget.php中的少量代码。如果没有做任何改动,那么就不需要再次回到数据库,这样可以减少数据库服务器的负载,并提高应用程序的性能。对象的用户并不一定需要理解其内部机制。如果软件开发团队中的某个高级成员编写了Widget类,那么他可以将对象交给一个初级成员,该初级成员可能并不能理解SQL,但是他仍然可以使用该对象,而不需要知道它的数据到底来自于什么位置以及如何保存所做的改动。实际上,可以将数据源从PostgreSQL数据库改成MySQL数据库,甚至是改成一个XML文件,而初级的团队成员甚至从来不知道或不必接触使用该类的任何代码。1.3.2 继承如果正在创建一个应用程序以处理某个轿车经销商的库存,那么可能需要一些诸如Sedan、PickupTruck和MiniVan这样的类,这些类对应于经销商库存中相同类型的汽车。应用程序可能不仅需要显示库存中有多少这些汽车,而且需要报告这些汽车的特征,这样销售人员就可以将这些信息告诉给顾客。私家轿车是四门轿车,可能需要记录其后座的空间和车厢容量。载货卡车并没有车厢,但却有一个具有一定容量的装货区,并且卡车本身还有一定的牵引能力(可以安全承载的最大载货量)。对于小型货车,可能需要列出拉门的数量(一个或两个)以及其中的座位数。但是,这些交通工具其实都只是不同类型的汽车,因此它们应该在应用程序中共享一些特征(如颜色、生产厂商、型号、生产年份、发动机标识号等)。为了保证每个类都具有这些相同的属性,可以只将创建这些属性的代码复制到包含类定义的每个文件中。本章前面提及,OOP方法的一个优点就是代码重用。因此,当然不需要复制代码,相反可以通过一个称为继承的过程来重用这些类的属性和方法。对于类来说,继承就是可以使用其父类的方法和属性的能力。利用继承机制,我们可以定义一个基类,在本例中为Automobile。可以认为其他的类也是一种Automobile类型,因此它们拥有了所有Automobile类型的类所具有的相同属性和方法。由于Sedan是一个Automobile类型的类,因此它自动继承了Automobile类所定义的所有内容,而不需要复制任何的代码。这样,您需要编写的仅仅是Sedan类中不是由所有汽车共享的附加属性和方法。换句话说,需要做的剩余工作仅仅是定义它们之间的不同之处而已;类之间的相似性是从基类继承得到的。重用代码的能力是继承的一个优点,但是使用继承还有另外一个主要优势。假设有一个名为Customer的类,而且该类具有buyAutomobile方法。该方法有一个参数;类Automobile的一个对象,而其内部的操作则是打印记录销售过程的文档,并且从库存系统中减少这种轿车的数量。由于所有的Sedan、PickupTruck和MiniVan都是Automobile,因此可以将这些类的对象传递给期望Automobile对象的函数。因为这3个特定的类型均继承自更为一般化的父类,所以它们都将具有相同的属性和方法集。只要需要的仅仅是所有Automobile共有的方法和属性,那么就可以接受任何继承自Automobile类的类的对象作为参数。以猫科动物为例。所有的猫科动物都共享一些属性,它们可以吃食物、睡觉、咕噜咕噜叫以及捕获猎物。它们还有另外一些共享的属性 重量、皮毛的颜色、胡须的长度和奔跑的速度。但是,狮子有一定长度的鬃毛(至少雄狮有),并且它们会发出咆哮声;印度豹有斑点;而驯养的猫科动物则没有任何这些特征。但是,所有这3种动物都是猫科动物。在PHP中,通过使用关键字extends可以指定一个类是另外一个类的子类。使用该关键字也告诉PHP正在声明的类应该从它的父类中继承所有的属性和方法,并且正在向该类中添加一些新的功能或提供某些附加的特殊内容。如果必须设计一个应用程序来管理动物园中的动物,那么可能需要有类Cat、Lion和Cheetah。在编写代码之前,在统一建模语言(Unified Modeling Language,UML)图中设计好类的层次关系,这样在编写这些类的代码和文档时就有据可依(我们将在第2章中详细讨论UML,因此即便不能完全理解此处所展示的内容,也不用担心)。类图应该给出一个父类Cat,以及两个子类Lion和Cheetah,这两个子类均继承于Cat。图1-1给出了该类图。图1-1 猫科动物的类图Lion和Cheetah类均继承自Cat,但是Lion类还实现maneLength属性和roar()方法,而Cheetah则添加了numberOfSpots属性。Cat类(class.Cat.php)应该实现如下:这个简单类建立了所有猫科动物都共有的属性和方法。为了创建Lion和Cheetah类,可以将Cat类中的所有代码都复制到名为Lion和Cheetah的类中。但是,这将产生两个问题。首先,如果在Cat类中发现了一个错误,那么必须记住同时在Lion和Cheetah类中进行修正。这就产生了更大的工作量,而不是更少的工作(但是减少工作量被认为是OO方法的基本优点之一)。其次,设想有其他某个类(可能是CatLover)的一个方法,该方法如下所示:public function petTheKitty(Cat $objCat) $objCat-purr();尽管把狮子或印度豹作为宠物来饲养可能不是一个非常安全的想法,但是如果想这么做的话,那么当您与它们靠得足够近时,它们也会发出咕噜咕噜的声音。应该能够将类Lion或Cheetah的一个对象传递给petTheKitty()函数。因此,必须采用其他的方式来创建Lion和Cheetah类,该方法使用了继承。通过使用关键字extends并设定被扩展的类的名称,可以方便地创建两个新类,这两个类具有与普通猫科动物一样的所有属性,但是同时提供了一些额外的功能。考虑下面的示例,可以将这段代码输入到class.Lion.php文件中:由于Lion类扩展了Cat类,因此现在可以做一些类似于如下的工作:weight = 200; /kg = s450 lbs. $objLion-furColor = brown; $objLion-maneLength = 36; /cm = s14 inches $objLion-eat(); $objLion-roar(); $objLion-sleep();?可以调用父类Cat的属性和方法,而不需要重新编写所有这些代码。记住,extends关键字告诉PHP:Lion类自动包含Cat类的所有功能,以及任何Lion所特有的属性或方法。该关键字同时告诉PHP:Lion对象也是一个Cat对象,并且现在可以使用类Lion的一个对象来调用petTheKitty()函数,即使函数声明使用Cat作为参数类型:petTheKitty($objLion);?使用这种方法,对Cat类执行的任何改动都将自动被Lion类继承。错误的修正、函数内部的改动或新的方法和属性都将一起传递给父类的子类。在某个大型的、具有良好设计的对象层次结构中,错误修正和添加增强功能都非常容易。对某个父类执行的细小改动可能会对整个应用程序产生巨大的影响。下一个示例将介绍一个自定义的构造函数如何用于扩展和特殊化一个类。创建一个新的文件并命名为class.Cheetah.php,在该文件中输入以下代码:maxSpeed = 100; ?在testcats.php中输入以下代码:maxSpeed purr(); else print Cant pet the kitty - its moving at . $objCat-maxSpeed . kilometers per hour!; $objCheetah = new Cheetah(); petTheKitty($objCheetah); $objCat = new Cat(); petTheKitty($objCat);?Cheetah类添加了一个名为numberOfSpots的新的公有成员变量和一个构造函数,该

温馨提示

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

评论

0/150

提交评论