作者:落伍hetty,写的很生动.学起来一点也不累,我转载过来.
好久没有来落伍了,既然来了就做点微薄的贡献吧。既然是教程,为什么说“玩”而不说“学”呢?问得好,因为说“学”的话,别人一看见标题基本就躲得远远的,根本懒得去点,说“玩”的话,估计还有那么一两个人来点,哈哈,好吧,我承认我是标题党,你赢了。
还是那句老话:我的个人能力有限,教程如果有什么疏漏和不足的地方,望大家多多见谅,也欢迎大家批评指正。
[目标读者]
本教程假设读者已经有了一定的PHP编程基础和经验,但还未开始接触PHP面向对象编程(以下简称OOP)。因此,如果你是小鸟或者大鸟,都可以选择直接路过了。
[教程宗旨]
这是一篇有悖常理的、大胆的教程,因为我要在不涉及或尽少涉及OOP概念的前提下,向读者介绍PHP的OOP。绝大部分PHPer对OOP敬而远之、退避三舍,就是被OOP那抽象繁琐、把人弄得焦头烂额的概念吓跑的,所以,本教程有意识的避开了OOP概念的讲解,力求通过实例用最简单、最通俗的方式来表述PHP的OOP。
[教程目录]
第1玩——————来玩类和对象的关系 (7楼)
第2玩——————来玩类的创建 (24楼)
第3玩——————来玩类的实例化 (29楼)
第4玩——————来玩类的访问 (33楼)
第5玩——————来玩类的构造方法和析构方法(48楼)
第6玩——————来玩类的封装(51楼)
第7玩——————来玩类的特殊方法(61楼)
第8玩——————来玩类的继承(75楼)
第9玩——————来玩类的重载(待续)
………………
(边写边补充吧)
由于时间的关系,我可能无法保证本教程有一个正常规律的更新速度,在此希望大家能够见谅,没有耐心的朋友可以等到教程连载完毕再看。
为了抓紧时间,我就直接在这里统一作下简单的回答。
首先是关于PHP的,PHP在很多大型项目中被运用,比如大家熟知的:
等等,由于篇幅关系,我就不多举例了。PHP在国内外大型项目中的运用是屡见不鲜的,它被证明强大是经受了无数实战考验的,每个人都有权质疑它的强大,但是在此之前,我们需要先抱着谦卑的态度去了解它。
这里融入一些个人感情,注意是个人的,我最喜欢、用得最多的服务端脚本语言就是PHP,也认为它是所有服务端脚本语言中最优秀的。
其次是关于PHP的OOP,这是仁者见仁智者见智的事情,PHP是混合型语言,这也可以看作是它的优势之一。
我们可以拿起锯子就做一把椅子,但是不经过计划拿起锯子就来建造一栋房子,房子的质量就没有保证了。
那么有位回帖的朋友就刚好说反了,OOP其实可以让PHP的代码更加简洁易懂,除此之外,更加易于维护,并且具有更强的重用性。
第1玩——————来玩类和对象的关系
本帖最后由 hetty 于 2010-7-9 10:07 编辑 假设我们要用PHP开发一个游戏……
“PHP能开发游戏吗?”
“不能吗?”
“你开发一个给我看看!”
“你给我钱嘛!”
“滚!”
(orz 不要再废话了,抓紧时间进入正题)好了,这个无聊的争论就不要继续了,我总不能拿按钮、文本域、表格这些东东当作对象给初学者们讲述OOP吧?那样大家会崩溃的,我也会崩溃的。为了增加本教程的易懂性、通俗性和趣味性……我决定了就假设我们要用PHP开发一个游戏,都说了,要玩嘛。首先声明OOP的概念是很重要的,因为它是OOP编程思想的基础,我在本教程里刻意的回避概念性问题,是为了降低初学者的恐惧心、增加初学者的求知欲,前面说了,很多不原意接触OOP的PHPer就是被概念折腾的。所以,大家千万不要跟人家说,啊,那个谁?Hetty说了,OOP概念算个屁,不用去管。OMG……那我岂不是成了罪人?本教程旨在先玩会OOP,再去了解OOP的概念。如果你没有玩过星际争霸和魔兽争霸的话,突然蹦出一个人就跟你大谈游戏平衡性的概念,就算他一套一套的跟你讲得头头是道,你势必也将是一头雾水,但如果是你玩过这两款游戏的话,根本不需要谁跟你讲,就算游戏平衡性的概念再飘逸,你自己也能在玩的过程中感觉到、体会到的。OOP也是一样,你自己悟出来的要比别人言传身教的清晰牢固千百倍,这就是本教程的出发点。至于吗?强调这么多遍……好吧,你赢了,先看图,在这个游戏里,我们将设计三个角色:
(就这水平了,将就着看吧)
红色是类,蓝色是对象。很多初学者经常把类和对象混淆,如果你是这类人,那么就多看一段时间,把这个图印在你心里,反复揣摩。如果你连思考都不愿意的话,那么就不必再往下看了,因为那是在浪费时间。
看好了吗?什么?一分钟都不到,回去继续看,是男人就持久点。
看出什么端倪了吗?我们可以看到红色的“角色”是空心的,因为它不是一个实体,只是个虚无缥渺的词汇,我们看不见摸不着,但是它却存在于我们心里;而蓝色“春哥“、”曾哥“和”凤姐“是实心的,因为它们是实体,是三个真真切切的人,我们看得见摸得着(如果敢摸的话),存在于我们的生活中。
在这个游戏中,你可以说“春哥”是一个“角色”,但绝对不能倒过来说“角色”是一个“春哥”,就像你不能说“水果”是一种“苹果”一样,不止是逻辑上说不过去,就连语法上也说不过去,这个……只需要小学语文水平就可以理解。
因此我们可以得出以下结论:
类 :是抽象的名称
对象:是具体的东东
其实类并不是什么新的概念,类,顾名思义,就是分类、种类、类别……我们从小就开始接触它了,习惯好的小朋友喜欢把自己的东西整理归类摆放好,比如变形金刚放一边啊、小人书放一边啊……这“变形金刚”和“小人书”就是类了啊。
既然我们主题是玩,那么就拿“变形金刚”这个类来说说,它是很笼统、很抽象的名称,而非一个实体。如果把它具体化,那么我们就可以得到实体的对象,比如“大黄蜂”、“擎天柱”、“铁皮”。
“大黄蜂”、“擎天柱”、“铁皮”用抽象点的话说就是“变形金刚”。
“变形金刚”用具体点的话说就是“大黄蜂”、“擎天柱”、“铁皮”。
因此我们可以得出以下结论:
类是对象的抽象化
对象是类的具体化
把一个类具体化以后可以得到多个对象,把多个对象抽象化后可以得到一个类。
这时有人就会问了,生活中的类我们很熟悉,很好区分啊,那么程序中的类该怎么区分呢?其实道理是一样的,我们把有相同特征的东东归为一类就可以了。比如“大黄蜂”、“擎天柱”、“铁皮”,他们有相同的特性(属性):有高度、有重量、有颜色;他们有相同的行为(方法):会变形、会说话、会走路。因此,在程序中,我们经常把具有相同属性和方法的对象归为一类。
每个对象都是唯一的,就算你有两个一模一样的“大黄蜂”,但是每个“大黄蜂”都是独立存在的个体,就像同名同姓的人很多,但是“春哥”却是这个世界上独一无二的(多了这个世界会灭亡的)。
程序与生活不同的地方:
在生活中,往往是把一组对象归为一类:先有了很多苹果的果实,我们才能把它们归为苹果类。
在程序中,往往是用类来产生一组对象:先有了角色类,我们才能创造出春哥、曾哥和凤姐来。
好了,今天就玩到这里,表达能力有限,不知道大家看明白没有?如果没有看明白的话,争取看明白,因为这篇看明白了,后面就将一马平川,越来越好玩。 |
回复
本帖最后由 hetty 于 2010-7-9 10:05 编辑 感谢大家参与,关于面向对象和面向过程,前面我已经表示得很清楚了,PHP是一种混合型语言,这也是它的优势,因此,我们可以拿起锯子就做一把椅子(面向过程),也可以经过周密的计划来建造一栋房子(面向对象),无论是面向对象还是面向过程,都有自己的优势,都要付出一定的代价。但是,我们还是要会玩面向对象才行啊,因为从某种意义上来讲,面向过程已经是一种过于”传统”的编程思想,毕竟面向对象更符合现实的逻辑。从PHP5开始,PHP已经完全支持面向对象,PHP6势必要加强对面向对象的支持。那么PHP官方为什么越来越重视面向对象呢?答案是不言而喻的。起初,PHP只是为个人和小项目而设计的脚本语言,但是现在不一样了,作为最优秀、最流行的客户端脚本语言之一,PHP的地位已经被越来越多的人认可,现今为止它在无数大型项目上的表现也令人感到十分满意,已经证明了它足以胜任任何大型项目。由于它是跨平台、免费的,因此被越来越多的人喜爱和接纳,尤其是JAVA阵营的朋友们。我们不妨来展望一下PHP的未来,通过AMFPHP,PHP与Flex、Flash、Json、XML等技术实现了二进制无缝通信。ActionScript3.0开始,也开始完全支持面向对象,再加上Flex和Flash助阵,我们不仅可以利用PHP来开发WEB应用程序、RIA应用程序、而且还可以开发移动应用程序(Flash逐渐开始在移动平台崭露头角,目前已经得到Android 2.2的支持,注意是真真正正的Flash,而非面向低端手机的Flash Lite,而这一切仅仅是一个开端),这对我国马上就要到来3G时代有着非凡的意义。因此,对于一个真正的PHPer来说,如果不接触面向对象,那的确是一种遗憾。 |
第2玩——————来玩类的创建
本帖最后由 hetty 于 2010-7-12 15:51 编辑 好,那么从今天开始,我们就要玩代码了,PHP的类里面大概包含两个部分,即它的成员属性和成员方法,不要紧张,我们不去挖它们的概念,所谓属性和方法,就是类里面的成员变量和成员函数。所以从现在开始,只要我提到属性,大家就要下意识的想到它是一个变量;只要我提到方法大家就要下意识的想到,它是一个函数。因此,我在这里给出一个狭义的结论,请大家牢记:属性就是变量
方法就是函数当然对外大家不能这么说,因为他们性质是截然不同的,由于我们回避了概念性的东西,这么说仅仅是为了便于大家记忆和理解。好,那么我们现在来玩一玩怎么创建一个完整的PHP类。这里可能会涉及到一些命名规范,关于这些规范,大家可以参阅我另外一篇文字《分享下我编程的代码规范》
http://www.im286.com/thread-4111157-1-1.html
创建类的方法:
- class 类的名称
- {
- var $属性的名称;
- function 方法的名称(参数列表)
- {
- ……
- }
- }
复制代码
这样就是一个完整的类,怎么样?够简单吧?它本来也就不难,只是很多人由于一些概念性问题不原意接去触它,但是一旦接触了,大家会发现,它也就不过如此嘛,的确,它也就不过如此。
一个类里面可以包含多个属性和多个方法。
那么我们结合前面假设开发的游戏来设计一个类:
- class Role //角色类
- {
- //属性部分
- var $role_name; //角色的名字
- var $role_sex; //角色的性别
- var $role_skill; //角色的技能
- //方法部分
- function roleTalk() //角色说话
- {
- echo ‘我会说话<br />’;
- }
- function roleFight() //角色发招
- {
- echo ‘我会发招<br />’;
- }
- }
复制代码
这样,一个具有完整属性和方法的类就算是建好了,是不是简单得掉渣呢?至于我们如何利用这个类来创造出“春哥”、“曾哥”和“凤姐”,并且让他们都会说话,都能发招呢?请看下玩分解。
|
第3玩——————来玩类的实例化
本帖最后由 hetty 于 2010-7-9 12:38 编辑 所谓实例化,就是前面我们提到的具体化,只不过实例化这个术语更专业一些,多认识些专业术语没有坏处,说起这个专业术语啊,这个……啊!这个这个……啊!我经常看到一些人,本来很简单的事情,被他们一口一个专业术语,把人家外行人(大部分是客户)忽悠得晕头转向,结果被忽悠的人还在那边一脸一个羡慕……我们不提倡忽悠人,但是我们得建立自己的防忽悠热线,所以多认识一些专业术语还是非常有必要的。扯远了,我们把类实例化以后就可以得到对象了,将类实例化的方法:
复制代码
结合前面的代码,我们要创建一个“春哥”,实际上就是要实例化一个名称为“春哥”的对象,代码如下:
- $brother_chun = new Role();
复制代码
到这里可能要有人感叹了,这PHP的OOP咋越玩越简单呢?没错,后面会越来越简单。为了方便大家查看,给出完整的代码如下:
- class Role //角色类
- {
- //属性部分
- var $role_name; //角色的名字
- var $role_sex; //角色的性别
- var $role_skill; //角色的技能
- //方法部分
- function roleTalk() //角色说话
- {
- echo ‘我会说话<br />’;
- }
- function roleFight() //角色发招
- {
- echo ‘我会发招<br />’;
- }
- }
- //将角色类实例化
- $brother_chun = new Role(); //创建一个名称为“春哥”的对象
- $brother_zeng = new Role(); //创建一个名称为“曾哥”的对象
- $sister_feng = new Role(); //创建一个名称为“凤姐”的对象
复制代码
当然我们还可以创建无数个其他人,虽然代码比较少,但是类的第一个好处代码重用已经显而易见了,我们可以通过一个类来创建无数个对象,设想如果一个类里面有成百上千行代码的话,我们可以节省多少行代码啊?
再设想,我们已经在游戏里创建了100个人,突然某天我们想要修改他们,如果不使用类创建的话,那么我们可能就要修改100个地方,而我们使用了类之后,仅仅只需要修改1个地方——修改我们的类即可,于是类的第二个好处易于维护也显而易见了,我们可以节省多少精力啊?
|
第4玩——————来玩类的访问
本帖最后由 hetty 于 2010-7-9 15:56 编辑 这个Discuz的编辑器好像有问题啊,我在段落的开头手动空两格,有些时候提交之后就被自动清除了……郁闷。那么我干脆不排版了,每段都顶头写,为了统一版面让大家看起来舒服点,前面的也全部改成这种样子好了。好,今天我们要玩的东西比较多,所以要集中精力哦。我们已经通过将类实例化来创建了“春哥”、“曾哥”和“凤姐”,但是他们还不能说话、还不能发招,怎么办呢?这就需要我们通过访问类的内容来实现。所谓访问,也是个专业术语,为了方便大家记忆和理解,大家只需要记住:访问就是使用
我们要使用“春哥”的特性,实际上就是要访问“春哥”的属性;
我们要使用“春哥”的行为,实际上就是要访问“春哥”的方法。
从类的内部访问属性和方法,我们要用this关键字。
访问属性:
复制代码
设置属性值(其实跟变量赋值是一个道理):
复制代码
访问方法:
复制代码
从类的外部访问属性和方法,我们要使用对象的名称。
访问属性:
复制代码
设置属性值(其实跟变量赋值是一个道理):
复制代码
访问方法:
复制代码
注意:在访问时,属性的名称是不带$号的,这一点,在《PHP 和 MySQL Web开发》(第三版)一书中的有些例子是错误的,虽然这本书被称为PHP的“圣经”,但是无论什么样书都难免出现人为的失误,所谓人非圣贤,孰能无过?所以俗话说尽信书不如无书,同样的提醒正在看我这篇教程的初学者们,尽信我不如无我,最重要的还是要大家自己去思考,培养独立思考的能力,不是吗?
光看上面的内容可能很难看出什么来,这很正常,没有关系,下面我们马上通过实例来说明它们的作用。
首先我们完善角色类,让它拥有一切比较实际的功能,这时,我们就要用到内部访问来修改它了:
- class Role //角色类
- {
- //属性部分
- var $role_name; //角色的名字
- var $role_sex; //角色的性别
- var $role_skill; //角色的技能
- //方法部分
- function roleTalk() //角色说话
- {
- echo $this->role_name.’说:’.’我是一个’.$this->role_sex.’人<br />’; //输出:(角色的名字)说:我是一个(角色的性别)人
- }
- function roleFight() //角色发招
- {
- echo $this->role_name.’使出了一招:’.$this->role_skill.'<br />’; //输出:(角色的名字)使出了一招:(角色的技能)
- }
- }
复制代码
为了方便讲解,每个方法里我只用了一个echo作为它的功能。当然如果你有时间和精力的话,可以去做一些更为复杂强大的功能。
接下来,如果我们要让“春哥”这个对象“活”起来,那么就要用外部访问了:
- //别忘了要一定先new一个名称为brother_chun的对象哦
- $brother_chun = new Role();
- //设置属性
- $brother_chun->role_name = ‘春哥’; //设置“春哥”的名字
- $brother_chun->role_sex = ‘男’; //设置“春哥”的性别
- $brother_chun->role_skill = ‘霸气菊花残’; //设置“春哥”的技能
- //访问方法
- $brother_chun->roleTalk(); //让“春哥”说话
- $brother_chun->roleFight(); //让“春哥”发招
复制代码
同理于“曾哥”和“凤姐”,为了方便大家查看,给出完整代码:
- class Role //角色类
- {
- //属性部分
- var $role_name; //角色的名字
- var $role_sex; //角色的性别
- var $role_skill; //角色的技能
- //方法部分
- function roleTalk() //角色说话
- {
- echo $this->role_name.’说:’.’我是一个’.$this->role_sex.’人<br />’; //输出:(角色的名字)说:我是一个(角色的性别)人
- }
- function roleFight() //角色发招
- {
- echo $this->role_name.’使出了一招:’.$this->role_skill.'<br /><br />’; //输出:(角色的名字)使出了一招(角色的技能)
- }
- }
- //“春哥”横空出世
- $brother_chun = new Role();
- //设置属性
- $brother_chun->role_name = ‘春哥’;
- $brother_chun->role_sex = ‘男’;
- $brother_chun->role_skill = ‘霸气菊花残’;
- //访问方法
- $brother_chun->roleTalk();
- $brother_chun->roleFight();
- //“曾哥”横空出世
- $brother_zeng = new Role();
- //设置属性
- $brother_zeng ->role_name = ‘曾哥’;
- $brother_zeng ->role_sex = ‘男’;
- $brother_zeng ->role_skill = ‘爷们菊花劫’;
- //访问方法
- $brother_zeng ->roleTalk();
- $brother_zeng ->roleFight();
- //“凤姐”横空出世
- $sister_feng = new Role();
- //设置属性
- $sister_feng ->role_name = ‘凤姐’;
- $sister_feng ->role_sex = ‘女’;
- $sister_feng ->role_skill = ‘知音故事会’;
- //访问方法
- $sister_feng ->roleTalk();
- $sister_feng ->roleFight();
复制代码
运行后得到结果如下:
春哥说:我是一个男人
春哥使出了一招:霸气菊花残
曾哥说:我是一个男人
曾哥使出了一招:绵阳菊花劫
凤姐说:我是一个女人
凤姐使出了一招:知音故事会
以此类推,你还可以让更多的人横空出世,让它们有不同的名字、不同的性别、不同的技能。
你也可以给它们加上其他的属性:比如攻击力、防御力、生命值、霸气值等等;
或者加上给它们加上其他的方法:比如会唱歌、会跳舞、会吃饭、会便便等等。
在这里由于时间和篇幅的关系,我们就不玩那么多了,大家要是有兴趣的话,自己下去慢慢玩吧。
这里着重讲一下this关键字的涵义,this顾名思义就是这个的意思,因为是在类的内部,它是被所有对象共用的,所以可以这么理解:这个对象调用了它,它就属于这个对象;哪个对象调用了它,它就属于哪个对象。
比如 $this->role_sex; 这句,“春哥”调用了它,在类的内部它就指“春哥”这个对象的性别;“曾哥”调用了它,在类的内部它就指“曾哥”这个对象的性别;“凤姐”调用了它,在类的内部它就指“凤姐”这个对象的性别,同理于其他。
好了,那么关于类最基本的玩法就介绍到这里,希望大家能够花一些宝贵的时间来慢慢消化一下今天我们玩过的东东。一定要好好消化哦,因为从下一玩开始,我们就要逐渐进入类的高级玩法了。
|
第5玩——————来玩类的构造方法和析构方法
本帖最后由 hetty 于 2010-7-12 01:33 编辑 所谓构造方法,就是在创建对象时自动执行的方法,也就是说,当你new一个对象的时候,不管你访不访问它,它都会自动执行。它是可以带参数的哦。构造方法通常用来执行一些初始化任务。构造方法的方法名称必须是 __construct ,注意前面是两个下划线,这是PHP5中的改进,在PHP4中,构造方法和其他语言一样,必须和类的名称相同,当然你要使用这种方式也可以,PHP5还是会认的,但是我不建议这么做,因为那样代码的可读性不强,而且万一某天你想修改类的名称时,你同时还得去修改构造方法的名称,因此我觉得PHP5这个小小的改进比其他语言显得更灵活些。
构造方法(建议使用)
- class Role //角色类
- {
- function __construct(参数列表); //构造方法
- {
- ……
- }
- }
复制代码
构造方法(不建议使用)
- class Role //角色类
- {
- function Role(参数列表); //构造方法
- {
- ……
- }
- }
复制代码
OK,我们来玩一下代码看看:
- class Role //角色类
- {
- //属性部分
- var $role_name; //角色的名字
- var $role_sex; //角色的性别
- var $role_skill; //角色的技能
- //方法部分
- function __construct($name,$sex,$skill) //构造方法,自动执行
- {
- $this->role_name = $name; //将参数传进来,给“姓名”属性一个初始值
- $this->role_sex = $sex; //将参数传进来,给“性别”属性一个初始值
- $this->role_skill = $skill; //将参数传进来,给“技能”属性一个初始值
- echo $this->role_name.’横空出世了<br /><br />’; //如果这句话被输出,说明构造方法自动执行了
- }
- function roleTalk() //角色说话
- {
- echo $this->role_name.’说:’.’我是一个’.$this->role_sex.’人<br />’; //输出:(角色的名字)说:我是一个(角色的性别)人
- }
- function roleFight() //角色发招
- {
- echo $this->role_name.’使出了一招:’.$this->role_skill.'<br /><br />’; //输出:(角色的名字)使出了一招(角色的技能)
- }
- }
- //实例化一个对象,同时将构造方法所需要的参数传入
- $brother_chun = new Role(‘春哥’,’男’,’霸气菊花残’);
- //属性设置(因为我们我们已经在构造方法中给了属性初始值,所以不需要设置了)
- //访问方法
- $brother_chun->roleTalk();
- $brother_chun->roleFight();
复制代码
运行后我们可以得到以下结果:
春哥横空出世了
春哥说:我是一个男人
春哥使出了一招:霸气菊花残
我们可以看到第一句话“春哥横空出世了”被输出了,虽然我们并没有访问构造方法,这是因为构造方法在创建对象时就被自动执行了。
虽然我们并没有在类的外部设置属性,但是后两句话依然输出了“春哥”的姓名、性别和技能属性,因为我们在构造方法中给了它们一个初始值,这个初始值就是我们传入的参数。当然,在有已经初始值的情况下,我们依然可以在类的外部重新设置“春哥”的属性。
好了,构造方法我们就完到这里,现在来我们开始玩析构方法,析构方法和构造方法正好相反,也就是在销毁对象时自动执行的方法,它是不带参数的,WHY?因为new一个对象的时候,后面只有一个括号,所以参数只能一个方法,而构造方法相对来说更加有用,所以就传给它咯,而且它可以为析构方法提供所需要的初始值。因此析构也不需要参数。析构方法通常用来执行一些善后工作。
构造方法的方法名称必须是 __destruct,注意前面也是两个下划线,这里要强调一下,PHP中凡是以双下划线开头的东东都有特殊作用。析构方法也是PHP5中的新改进,但是PHP4中没有析构方法,所以请不要尝试在PHP4中使用析构方法,否则电脑爆炸,后果自负,呵呵。
析构方法
- class Role //角色类
- {
- function __destruct(); //析构方法
- {
- ……
- }
- }
复制代码
那么结合前面的代码,我们把析构方法加进去,也来玩玩析构方法的代码吧:
- class Role //角色类
- {
- //属性部分
- var $role_name; //角色的名字
- var $role_sex; //角色的性别
- var $role_skill; //角色的技能
- //方法部分
- function __construct($name,$sex,$skill) //构造方法,自动执行
- {
- $this->role_name = $name; //将参数传进来,给“姓名”属性一个初始值
- $this->role_sex = $sex; //将参数传进来,给“性别”属性一个初始值
- $this->role_skill = $skill; //将参数传进来,给“技能”属性一个初始值
- echo $this->role_name.’横空出世了<br /><br />’; //如果这句话被输出,说明构造方法自动执行了
- }
- function roleTalk() //角色说话
- {
- echo $this->role_name.’说:’.’我是一个’.$this->role_sex.’人<br />’; //输出:(角色的名字)说:我是一个(角色的性别)人
- }
- function roleFight() //角色发招
- {
- echo $this->role_name.’使出了一招:’.$this->role_skill.'<br /><br />’; //输出:(角色的名字)使出了一招(角色的技能)
- }
- function __destruct() //析构方法,自动执行
- {
- echo $this->role_name.’打完收招,只见天上的浮云组成了一个“纯”字’; //如果这句话被输出,说明析构方法自动执行了
- }
- }
- //实例化一个对象,同时将构造方法所需要的参数传入
- $brother_chun = new Role(‘春哥’,’男’,’霸气菊花残’);
- //属性设置(因为我们我们已经在构造方法中给了属性初始值,所以不需要设置了)
- //访问方法
- $brother_chun->roleTalk();
- $brother_chun->roleFight();
复制代码
运行后我们可以得到以下结果:
春哥横空出世了
春哥说:我是一个男人
春哥使出了一招:霸气菊花残
春哥打完收招,只见天上的浮云组成了一个“纯”字
好啦,构造函数和析构函数我们就玩到这里,准备去看世界杯决赛了,虽然对于身为巴迷的我来说,巴西队被淘汰我的世界杯也就结束了,但是本届世界杯欧式足球的巅峰对决也不能错过啊。唉,邓叔啊邓叔,你硬是要活生生的把桑巴军团变成欧式球队吗?
|
第6玩——————来玩类的封装
本帖最后由 hetty 于 2010-7-12 16:55 编辑 这里给大家一个小小的友情提示,由于论坛里的代码没有颜色提示,而代码里又含有大量详细的注释,虽然我已经仔细书写代码格式,但是在颜色相同的情况下,可能仍然不便于阅读。因此在代码太长导致大家眼花缭乱的时候,大家可以把代码复制下来,到有颜色提示的编辑器里阅读,比如Zend Studio、Deamweaver、Eclipse等等,这样感觉会好很多。
好,那么我们今天开始玩类的封装,什么是封装呢?额……就是封起来装(汗), 其实大部分专业术语也很好从字面上的意思去理解,只不过绝大多数人一涉及到技术方面的问题,就习惯性的往高深的方向去想,其实没有必要,我们要尽量以我们自己容易理解的方式去想,这才是玩道,因为它们本就不是什么高深莫测的东西。
封装的作用是不允许别人随意从类的外部使用属性和方法,只留给别人一些有限的属性和方法去使用。OOP是鼓励使用封装的,那么到底封装有什么好处呢?很多书对于封装的好处三言两语就带过了,十分含糊,我在这里会尝试着给大家讲明白。
比如大家熟知的国内品牌机联想电脑,它就属于一种封装,对于不懂硬件的朋友来说,CPU、显卡、主板、硬盘、内存这些乱七八糟的硬件都被机箱封装起来了。机箱里面到底有些什么硬件?这些硬件对于电脑来说有些什么作用?它们到底是如何协同运转的?我们不需要去关心!因为我们只要会开机、会关机、会玩游戏就好了,这样我们的目的就达到了!但同时机箱里又提供了光驱、电源等有限的东西,方便我们可以从外部直接使用它们。
所以你说,一个封装的类,你拿过来,只需要知道怎么使用就可以了,根本不需要去关心它内部是怎么实现功能的,根本不需要去瞅那复杂的代码一眼,这是对于使用者来说的。
那么对于开发者来说呢?很多时候开发者不希望使用者能够随意去碰类内部的东西,因为那样可能会导致一些不可预料的事情发生,就像不懂硬件的朋友把机箱拆开,把里面硬件胡乱的拔来插去,那么电脑可能就无法启动,甚至烧坏,爆炸也说不定,这肯定不是开发者愿意看到的事情。
通常一个完整的系统是由无数个封装类构成的,它们每个都是一个独立的部分,但是又可以相互相通,这对于于团队开发、分工协作是十分有帮助的,比如说你做角色,我做地图,我要用角色的时候就直接就拿过来用,谁有闲功夫管你角色类里面都写了些什么代码,我自己的工作都还忙不过来呢;而且还大大的减少了排错的难度,就像电脑坏了,显卡坏了就修显卡,内存坏了就修内存,不需要把整个机箱里的硬件从头到尾修一遍,那还不如丢掉重新买一台。
所以说,我们要把尽多的工作交给类完成,把尽少的工作交给人完成,这才是正确的OOP主导思想。在类里面,属性和方法通常有公有的和私有的,又是些专业术语,不要紧张,我再次把它们通俗化:
公有就是允许别人用
私有就是不许别人用
在PHP中,类的属性和方法默认是公有的,但是我们不建议用默认的方式来定义公有的属性和方法,因为那不利于代码的可读性,我们从小要养成良好的编程习惯,我们要习惯用public访问修饰符来声明某个属性和方法是公有的。
请看下面两例代码,它们实际上是一样的,只不过一个是默认的,另一个是我们通过访问修饰符定义的。
默认方式(不建议使用)
- class 类的名称
- {
- var $属性的名称; //没有访问修饰符,有var关键字
- function 方法的名称(参数列表) //没有访问修饰符
- {
- ……
- }
- }
复制代码
声明方式(建议使用)
- class 类的名称
- {
- public $属性的名称; //有访问修饰符,var关键字被去掉了
- public function 方法的名称(参数列表) //有访问修饰符
- {
- ……
- }
- }
复制代码
注意:声明属性的时候,我们先要去掉var关键词,然后再前面加上访问修饰符public;声明方法的时候,直接在前面加上访问修饰符public即可。
我们来认识一下PHP类中的三个访问修饰符:
访问修饰符 |
声明类型 |
允许外部使用 |
允许子类使用 |
public |
公有 |
是 |
是 |
protected |
私有 |
|
是 |
private |
私有 |
|
|
从上表我们可以看到,通过protected和private这两个访问修饰符可以将属性和方法声明为私有的,即将它们封装起来,不许别人用。它们的区别仅仅在于对子类的访问控制,关于子类我们会在后面类的继承里面讲到,现在不用去管它,我们现在的首要任务是跟这三个访问修饰符混个熟脸。
好,我们来玩一下代码,看看是不是真的是这么一回事情:
- class Role //角色类
- {
- //属性部分
- public $role_name; //角色的名字,请尝试将public换成protected或private进行封装
- //方法部分
- public function roleTalk() //角色说话,请尝试将public换成protected或private进行封装
- {
- echo ‘我会说话’;
- }
- }
- //实例化一个对象
- $brother_chun = new Role();
- //设置属性
- $brother_chun->role_name = ‘春哥’;
- //访问方法
- $brother_chun->roleTalk();
复制代码
上面这段代码将属性和方法声明为public时,程序运行正常;若尝试将属性或方法的访问修饰符换成protected和private,那么程序就会报错。因为私有的属性和方法是不允许从类的外部使用的。大家可以自己去亲自试验一下,体会一下,这样可以加深理解。那……今天就玩到这里吧。
|
第7玩——————来玩类的特殊方法
Sorry,最近比较忙,比较忙比较忙……没有及时更新,希望大家多多谅解,这些特殊方法你也可以叫它们做特殊函数,因为我们前面说过了,方法就是类里面的函数。实际上前面我们已经玩过两个特殊方法了,还记得吗?就是构造方法和析构方法,即__construct和__destruct这两个方法。那么希望大家记住,PHP类里面凡是以双下划线开头的方法,都有特殊作用,并且都会自动执行。今天我们就来玩另外四个特殊方法,它们分别是:__set()、__get()、__isset()和__unset()
大家注意,之前因为还没有讲到这些特殊方法,同时也是为了方便演示,很多代码中属性都是用默认的public作为声明的,既然现在我们开始玩这些新知识了,那么在此声明,我们不建议将属性声明为默认的public。
一般情况下,直接从类的外部访问和设置属性是个很糟糕的想法,因为我们说过了,OOP的优点是封装,而且鼓励使用封装,因此我们在声明属性的时候,应该使用private或protected,这更符合OOP的编程思想和现实的逻辑。但是,很多情况下我们仍然需要访问和设置属性,而且是经常性的,于是PHP为我们提供了两个方法,用__get()来访问属性,用__set()来设置属性。
我们先通过代码来看看它们是怎么用的,然后再来分析,请结合注释仔细看:
- class Role //角色类
- {
- //属性部分
- private $role_name; //封装:角色的名字
- private $role_sex; //封装:角色的性别
- private $role_skill; //封装:角色的技能
- //方法部分
- private function __get($attribute_name) //允许访问属性(有一个参数:传入属性的名称)
- {
- if($attribute_name == ‘role_name’ || $attribute_name == ‘role_sex’) //如果是:名字、性别
- {
- return $this->$attribute_name; //就允许访问
- }
- }
-
- private function __set($attribute_name,$attribute_value) //允许设置属性(有两个参数:传入属性的名称和值)
- {
- if($attribute_name == ‘role_name’ || $attribute_name == ‘role_sex’) //如果是:名字、性别
- {
- $this->$attribute_name = $attribute_value; //就允许设置
- }
- }
- }
- //将角色类实例化得到一个对象
- $brother_chun = new Role();
- //设置属性
- $brother_chun->role_name = ‘春哥’; //可以设置:姓名
- $brother_chun->role_sex = ‘男’; //可以设置:性别
- $brother_chun->role_skill = ‘霸气菊花残’; //无法设置:技能
- //访问属性
- echo $brother_chun->role_name.'<br />’; //可以访问:姓名
- echo $brother_chun->role_sex.'<br />’; //可以访问:性别
- echo $brother_chun->role_skill.'<br />’; //无法访问:技能
复制代码
不要看到代码就烦哦,我刚学编程的时候也是这样,这是个坏习惯,因为无论你的理论基础有多么的扎实,如果看不懂代码那也是白搭,更不要说去写了。养成分析代码的好习惯,尤其是去看别人写的优秀的代码,那样会使自己进步非常快。
注意:这里参数是带$号的,关于这个问题貌似“PHP圣经”又写错了几个例子,在此提醒大家,请与属性做好区别。
参数:$this->$something
属性:$this->something
__get()方法用于访问属性,当我们试图访问一个属性的时候,它会自动将属性的名称作为参数传入,并且返回属性的值。
__set()方法用于设置属性,当我们试图设置一个属性的时候,它会自动将属性的名称和值作为参数传入并执行设置操作,没有返回值。
通过运行代码我们会发现,虽然所有属性都被声明为private,即封装为私有属性,但是通过__get()方法和__set()方法,我们可以允许某些属性(姓名、性别)能够被访问或设置,并且可以定制一些个性化的条件控制属性的访问和设置,比如我们只允许把性别设置成“男”或“女”,而不能设置为“人妖”等等。如果我们删除这两个特殊方法,试图直接访问或设置属性,那么程序就会报错。
好,那么这两个特殊方法就玩到这里,请大家多看代码和注释,并且尝试修改程序,通过这种方式来进一步了解它们。
下面玩__isset()和__unset()这两个特殊方法,我们在原有代码的基础上新增几行代码:
- class Role //角色类
- {
- //属性部分
- private $role_name; //封装:角色的名字
- private $role_sex; //封装:角色的性别
- private $role_skill; //封装:角色的技能
- //方法部分
- private function __get($attribute_name) //允许访问属性(有一个参数:传入属性的名称)
- {
- if($attribute_name == ‘role_name’ || $attribute_name == ‘role_sex’) //如果是:名字、性别
- {
- return $this->$attribute_name; //就允许访问
- }
- }
-
- private function __set($attribute_name,$attribute_value) //允许设置属性(有两个参数:传入属性的名称和值)
- {
- if($attribute_name == ‘role_name’ || $attribute_name == ‘role_sex’) //如果是:名字、性别
- {
- $this->$attribute_name = $attribute_value; //就允许设置
- }
- }
- //新增部分
- private function __isset($attribute_name) //检测属性是否设置(有一个参数:传入属性的名称)
- {
- return $this->$attribute_name; //返回结果
- }
-
- private function __unset($attribute_name) //删除属性(有一个参数:传入属性的名称)
- {
- unset($this->$attribute_name); //进行删除
- }
- }
- //将角色类实例化得到一个对象
- $brother_chun = new Role();
- //设置属性
- $brother_chun->role_name = ‘春哥’; //可以设置:姓名
- $brother_chun->role_sex = ‘男’; //可以设置:性别
- $brother_chun->role_skill = ‘霸气菊花残’; //无法设置:技能
- //访问属性
- echo $brother_chun->role_name.'<br />’; //可以访问:姓名
- echo $brother_chun->role_sex.'<br />’; //可以访问:性别
- echo $brother_chun->role_skill.'<br />’; //无法访问:技能
- //新增部分
- echo var_dump(isset($brother_chun->role_name)); //检测并输出变量是否被设置
- unset($brother_chun->role_name); //删除此属性
- echo $brother_chun->role_name.'<br />’; //由于属性被删除,因此没有输出
复制代码
__isset()和__unset()这两个特殊方法的原理和__get()和set()方法的原理是一样的,即将属性作为参数传入,并在类的内部进行处理。说白了,这两个特殊方法就是为了方便在属性被封装的情况下,允许在类的外部使用isset()和unset()这两个PHP函数,更多的也没有什么好说的了,大家看代码和注释吧,尝试着修改运行看看,OK,今天就玩到这里。
|
第8玩——————来玩类的继承
本帖最后由 hetty 于 2010-7-20 03:09 编辑
继承是怎么一个情况呢?和遗传有点相似,但是又不完全相同,比如儿子可以继承父亲的特征,而且还可以拥有自己的特征;同样的子类可以继承父类的属性和方法,而且还可以拥有自己的属性和方法。但是要注意一点,类的世界里面只有”父子关系“,毕竟类不是真实的动物,会有公的和母的之分,还要交配才产下子类,没有这种事情,这只是一个形象的比喻而已。由一个基本的类派生出另一个类,那么这个基本的类就是父类,它所派生出来的类就叫做子类,所以请看下面。
父类也可以叫超类
子类也可以叫派生类
如果你觉得我有性别歧视的嫌疑,当然你也可以叫它们母类和女类,怎么叫无所谓,反正它们是“单性繁殖”,只要理解这点就可以了。想想平时你接触得最多的文件夹,根目录和子目录的关系就和这个道理差不多了……连我自己都觉得啰嗦了,主要是多照顾初学者朋友们,先上图吧,看看更健康。
在PHP和JAVA等大多数语言中,类只允许单继承,也就是说一个子类只能有一个父类;而在C++等一些语言中,类允许多继承,也就是说一个子类可以有多个父类。我个人觉得单继承更符合现实的逻辑,而且我们现在玩的是PHP的OOP,就不对多继承进行过多的讨论了。
前面说了,我个人觉得多继承是不符合现实的逻辑的,就是上图所示的样子。但是多继承这个功能是十分有用的,所以PHP为我们引入了接口,接口可以解决多继承的问题,同时又符合了现实的逻辑。关于接口这个东东,我们会在后面的章节中讲解。
现在图看完了,那么打起精神来看代码吧,为了便于演示和便于大家理解,我把所有属性和方法都声明为了public,再次重声,我们不建议这么做:
- class Role //角色类
- {
- //属性部分
- public $role_name; //名字
- //方法部分
- public function roleTalk() //说话
- {
- echo $this->role_name.’:我会说话<br />’;
- }
-
- public function roleFight() //发招
- {
- echo $this->role_name.’:我会发招<br />’;
- }
- }
- class RoleSon extends Role //角色儿子类 继承 角色类
- {
- //方法部分
- public function roleEat() //吃饭(儿子类自己的方法)
- {
- echo $this->role_name.’:我会吃饭(儿子特有)<br />’;
- }
- }
- class RoleGrandson extends RoleSon //角色孙子类 继承 角色儿子类
- {
- //方法部分
- public function roleShit() //拉屎(孙子类自己的方法)
- {
- echo $this->role_name.’:我会拉屎(孙子特有)<br />’;
- }
- }
- //实例化角色类
- $brother_chun = new Role();
- $brother_chun->role_name = ‘春哥’;
- $brother_chun->roleTalk();
- $brother_chun->roleFight();
- //实例化角色儿子类
- $brother_chun_son = new RoleSon();
- $brother_chun_son->role_name = ‘春哥之子’;
- $brother_chun_son->roleTalk();
- $brother_chun_son->roleFight();
- $brother_chun_son->roleEat();
- //实例化角色孙子类
- $brother_chun_grandson = new RoleGrandson();
- $brother_chun_grandson->role_name = ‘春哥之孙’;
- $brother_chun_grandson->roleTalk();
- $brother_chun_grandson->roleFight();
- $brother_chun_grandson->roleEat();
- $brother_chun_grandson->roleShit();
复制代码
从上面的代码我们已经可以看出继承一个类的步骤非常简单,在子类的名称后面加上extends关键词,再跟上所要继承的父类的名称即可:
- class 子类的名称 extends 父类的名称
- {
- ……
- }
复制代码
我们来看看示例代码的输出结果:
- 春哥:我会说话
- 春哥:我会发招
- 春哥之子:我会说话
- 春哥之子:我会发招
- 春哥之子:我会吃饭(儿子特有)
- 春哥之孙:我会说话
- 春哥之孙:我会发招
- 春哥之孙:我会吃饭(儿子特有)
- 春哥之孙:我会拉屎(孙子特有)
复制代码
我们能发现子类不但可以继承父类的属性和方法,也可以拥有自己的属性和方法。如果我们尝试用“春哥”去访问子类的特有方法,那么程序就会报错。
“春哥之孙”虽然仅仅是继承了“春哥之子”,但是由于“春哥之子”继承了“春哥”,所以“春哥之孙”同时拥有它两个祖宗的属性和方法。
所以说继承这个东东是十分好用啊,你可以利用一个父类来派生出无数个功能更加强大或有特殊用途的子类来。比如把“人类”定义为一个父类,那么通过类的继承,按照性别就可以派生出“男人类”和“女人类”,按照人品就可以派生出“好人类”和“坏人类”,按照职业就可以派生出“工人类”和“农民类”……如此这般,他们同是人类,但又有各自的特点。
以上,什么代码重用易于维护之类老生常谈的好处我就不多说了,大家自己慢慢体会吧,今天就玩到这里。 |
第9玩——————来玩类的重载
比较忙,很久没有更新了,希望大家见谅。在PHP中,函数是不能重名的,同一个类中方法也不能重名,我们这里所说的重载,是指子类方法与父类方法以同名的方式对父类方法进行覆盖。很多时候,子类可以继承父类的方法直接拿过来用,但是有一些时候,子类需要覆盖父类原有的方法,为自己扩展一些新功能。我们通过代码来分析,同样要注意看注释部分哦:class Role //角色类
{
//属性部分
public $role_name; //名字
//方法部分
public function roleTalk() //说话
{
echo ‘我的名字叫:’.$this->role_name.’
‘;
}
}
class RoleSon extends Role //角色儿子类 继承 角色类
{
//属性部分
public $role_nickname; //小名(新的属性)
//方法部分
public function roleTalk() //说话(重载覆盖)
{
echo ‘我的名字叫:’.$this->role_name.’
‘;
echo ‘我的小名叫:’.$this->role_nickname.’
‘;
}
}
//实例化角色类
$brother_chun = new Role();
$brother_chun->role_name = ‘春哥’; //名字
$brother_chun->roleTalk(); //父类原有方法
//实例化角色儿子类
$brother_chun_son = new RoleSon();
$brother_chun_son->role_name = ‘春哥之子’; //名字
$brother_chun_son->role_nickname = ‘狗胜’; //小名(儿子特有)
$brother_chun_son->roleTalk(); //子类覆盖方法
复制代码以上代码将输出:
我的名字叫:春哥
我的名字叫:春哥之子
我的小名叫:狗胜
通过上面的代码我们可以看到,子类里有一个新的属性$role_nickname,这个子类里的属性是无法被父类的roleTalk()方法访问的,如果子类通过继承直接使用roleTalk()方法话,那么新的属性就会毫无用处了,因此我们可以通过在子类里建立一个与父类同名的方法来重载,即覆盖父类原有的方法来达到我们的目的——让春哥之子说出自己的小名。
那么有人可能就要问了,我们在子类重新定义一个说话的新方法,比如newRoleTalk()不就得了?效果还不是一样。但是我必须告诉大家,最好不要这么做,因为这不符合现实的逻辑,就好像一个人只有一个嘴巴,我们不可能为了让人多说一句话就多长一个嘴巴,设想如果春哥真长出了N个嘴巴,我们就不用等到2012世界末日了对不对?
那么又有人要问了,roleTalk()原有方法里面只有一行代码,我们要利用重载来扩展一些新功能十分容易搞定,如果roleTalk()里面有成百上千行代码的话,而且父类代码是封装的我们无法查看,那事情不就大条了?的确是这样的,但是不要着急,PHP为我们提供了完美的解决方案来调用父类方法的原有代码,只需要在重载时添加新的代码即可,方法如下:
方法一(建议使用)parent::方法的名称();
复制代码方法二(不建议使用)父类的名称::方法的名称();
复制代码完整代码如下:class Role //角色类
{
//属性部分
public $role_name; //名字
//方法部分
public function roleTalk() //说话
{
echo ‘我的名字叫:’.$this->role_name.’
‘;
}
}
class RoleSon extends Role //角色儿子类 继承 角色类
{
//属性部分
public $role_nickname; //小名(新的属性)
//方法部分
public function roleTalk() //说话(重载)
{
parent::roleTalk(); //父类方法原有的代码
echo ‘我的小名叫:’.$this->role_nickname.’
‘; //子类方法新增的代码
}
}
//实例化角色类
$brother_chun = new Role();
$brother_chun->role_name = ‘春哥’; //名字
$brother_chun->roleTalk(); //父类原有方法
//实例化角色儿子类
$brother_chun_son = new RoleSon();
$brother_chun_son->role_name = ‘春哥之子’; //名字
$brother_chun_son->role_nickname = ‘狗胜’; //小名(儿子特有)
$brother_chun_son->roleTalk(); //子类覆盖方法
复制代码以上代码依然将输出:
我的名字叫:春哥
我的名字叫:春哥之子
我的小名叫:狗胜
这样一来,我们就可以轻松的利用重载为原有方法扩展新功能了。OK,那么今天就玩到这里,再次感谢大家的支持,我会抽时间尽力尽快更新的。