CODE:<?php
class Room
{
public $name;
function __construct($name="unnamed")
{
$this->name = $name;
}
}
class House
{
//array of rooms
public $room;
}
//create empty house
$home = new house;
//add some rooms
$home->room[] = new Room("bedroom");
$home->room[] = new Room("kitchen");
$home->room[] = new Room("bathroom");
//show the first room of the house
print($home->room[0]->name);
?>
PHP有两个特别的定名空间arent定名空间指向父类,self定名空间指向以后的类. 例子6.6中显示了若何用parent定名空间来挪用父类中的机关函数. 同时也用self来在机关函数中挪用另外一个类办法.
CODE:<?php
class Animal //植物
{
public $blood; //热血or冷血属性
public $name;
public function __construct($blood, $name=NULL)
{
$this->blood = $blood;
if($name)
{
$this->name = $name;
}
}
}
class Mammal extends Animal //哺乳植物
{
public $furColor; //外相色彩
public $legs;
function __construct($furColor, $legs, $name=NULL)
{
parent::__construct("warm", $name);
$this->furColor = $furColor;
$this->legs = $legs;
}
}
class Dog extends Mammal
{
function __construct($furColor, $name)
{
parent::__construct($furColor, 4, $name);
self::bark();
}
function bark()
{
print("$this->name says 'woof!'");
}
}
$d = new Dog("Black and Tan", "Angus");
?>
第四章中引见了若何挪用函数. 关于对象的成员来是如许挪用的:假如你需求在运转时肯定变量的称号,你可以用$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
PHP代码:
CODE:<?php
class Widget
{
private $name;
private $price;
private $id;
public function __construct($name, $price)
{
$this->name = $name;
$this->price = floatval($price);
$this->id = uniqid();
}
//checks if two widgets are the same 反省两个widget是不是不异
public function equals($widget)
{
return(($this->name == $widget->name)AND
($this->price == $widget->price));
}
}
$w1 = new Widget('Cog', 5.00);
$w2 = new Widget('Cog', 5.00);
$w3 = new Widget('Gear', 7.00);
//TRUE
if($w1->equals($w2))
{
print("w1 and w2 are the same<br>\n");
}
//FALSE
if($w1->equals($w3))
{
print("w1 and w3 are the same<br>\n");
}
//FALSE, == includes id in comparison
if($w1 == $w2) //不等,由于ID分歧
{
print("w1 and w2 are the same<br>\n");
}
?>
假如你对面向对象编程不熟习,你能够想晓得用private成员的目标是甚么. 你可以回想一下封装和耦合的设法,这在本章开首咱们有会商过. Private成员有助于封装数据. 他们可以埋没在一个类外部而不被类内部的代码接触到. 同时他们还有助于完成松懈的耦合. 假如数据布局外的代码不克不及直接会见外部属性,那末就不会发生一个隐性的联系关系性.
固然,大局部private属性依然可以被内部代码同享. 处理办法是用一对public办法,一个是get(获得属性的值),另外一个是set(设置属性的值). 机关函数也承受属性的初始值. 这使得成员间的交换经由过程一个狭小的,经由优秀限制的接口来停止. 这也供应改动传递给办法的值的时机. 注重在例子6.8中,机关函数若何强迫使price成为一个float数(floadval()).
Protected(受回护的) 成员能被同个类中的一切办法和承继出的类的中一切办法会见到. Public属性有背封装的精力,由于它们答应子类依附于一个特定的属性来书写.protected办法则不会带来这方面的担心.一个利用protected办法的子类需求很清晰它的父类的布局才行.
例子6.9由例子6.8改善而失掉,包括了一个Widget的子类Thing. 注重Widget如今有一个叫作getName的protected办法. 假如Widget的实例试图挪用protected办法将会失足: $w1->getName()发生了一个毛病. 但子类Thing中的getName办法可以挪用这个protected办法.固然关于证实Widget::getName办法是protected,这个例子显得过于复杂. 在实践情形下,利用protected办法要依附于对对象的外部布局的了解.
Listing 6.9 Protected members
PHP代码:
CODE:<?php
class Widget
{
private $name;
private $price;
private $id;
public function __construct($name, $price)
{
$this->name = $name;
$this->price = floatval($price);
$this->id = uniqid();
}
//checks if two widgets are the same
public function equals($widget)
{
return(($this->name == $widget->name)AND
($this->price == $widget->price));
}
protected function getName()
{
return($this->name);
}
}
class Thing extends Widget
{
private $color;
public function setColor($color)
{
$this->color = $color;
}
public function getColor()
{
return($this->color);
}
public function getName()
{
return(parent::getName());
}
}
$w1 = new Widget('Cog', 5.00);
$w2 = new Thing('Cog', 5.00);
$w2->setColor('Yellow');
//TRUE (still!) 了局依然为真
if($w1->equals($w2))
{
print("w1 and w2 are the same<br>\n");
}
//print Cog 输入 Cog
print($w2->getName());
?>
一个子类能够改动经由过程覆写父类办法来改动办法的会见体例,虽然如斯,依然有一些限制. 假如你覆写了一个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 静态绑定
PHP代码:
CODE:<?php
class Father
{
protected $salutation = "Hello there!"; //问候
public function getSalutation()
{
print("$this->salutation\n");
$this->identify();
}
protected function identify()
{
print("I am Father.<br>\n");
}
};
class Son extends Father
{
protected $salutation = "Hey!"; //父类中的protected $salutation 被覆写
protected function identify() //父类中的protected identify() 被覆写
{
print("I am Son.<br>\n");
}
};
$obj = new Son();
$obj->getSalutation(); //输入Hey! I am Son.
?>
//注: 在子类中没有覆写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
PHP代码:
CODE:<?php
class Father
{
private $salutation = "Hello there!";
public function getSalutation()
{
print("$this->salutation\n");
$this->identify();
}
private function identify()
{
print("I am Father.<br>\n");
}
}
class Son extends Father
{
private $salutation = "Hey!";
private function identify()
{
print("I am Son.<br>\n");
}
}
$obj = new Son();
$obj->getSalutation(); //输入Hello there! I am Father.
?>
静态绑定的优点是答应承继类来改动父类的行动,同时可以坚持父类的接口和功效. 看例子6.12. 因为利用了静态绑定,在deleteUser中被挪用的isAuthorized的version 可以由对象的类型来肯定. 假如是一个通俗的user,PHP挪用User::isAuthorized会前往FALSE.假如是一个AuthorizedUser的实例,PHP挪用AuthorizedUser::isAuthorized,将答应deleteUser顺遂履行.
//haohappy注:用一句话说清晰,就是对象类型与办法,属性绑定. 挪用一个父类与子类中都存在的办法或会见一个属性时,会先判别实例属于哪一种对象类型,再挪用响应的类中的办法和属性.
Listing 6.12 静态绑定的优点
PHP代码:
CODE:<?php
class User //用户
{
protected function isAuthorized() //是不是是验证用户
{
return(FALSE);
}
public function getName() //取得名字
{
return($this->name);
}
public function deleteUser($username) //删除用户
{
if(!$this->isAuthorized())
{
print("You are not authorized.<br>\n");
return(FALSE);
}
//delete the user
print("User deleted.<br>\n");
}
}
class AuthorizedUser extends User //认证用户
{
protected function isAuthorized() //覆写isAuthorized()
{
return(TRUE);
}
}
$user = new User;
$admin = new AuthorizedUser;
//not authorized
$user->deleteUser("Zeev");
//authorized
$admin->deleteUser("Zeev");
?>
为何private的类成员摹拟静态绑定? 为了回覆这个成绩, 你需求回想一下为何需求有private成员.甚么时分用它们来取代protected成员是成心义的?
private成员只要当你不想让子类承继改动或特别化父类的行动时才用到. 这类情形比你想像的要少. 凡是来讲,一个好的对象分层布局应该答应绝大多半功效被子类特别化,改善,或改动―这是面向对象编程的基本之一. 必定的情形下需求private办法或变量,例如当你确信你不想答应子类改动父类中的某个特定的部分.
CODE:<?php
$a = 5;
//$b points to the same place in memory as $a $b与$a指向内存中同个地址
$b = &$a;
//we're changing $b, since $a is pointing to 改动$b,指向的地址改动
//the same place - it changes too $a指向的地址也改动
$b = 7;
//prints 7 输入7
print $a;
?>
因为构建一个指向彼此的对象收集是一切面向对象设计形式的基本,这个改善具有十分严重的意义.当援用答应创立更多壮大的面向对象使用法式, PHP看待对象和其它类型数据不异的做法带给开辟者极大的疾苦.就像任何PHP4的法式员将会告知你的, 使用法式将会遭受WTMA(Way Too Many Ampersands过量&)综合症. 假如你想构建一个实践使用,你会感应极其疾苦,看看例6.21你就分明.
Listing 6.21 Problems with objects in PHP 4 PHP4中利用对象的成绩