這篇文章介紹在PHP的面向對象編程(OOP)。我將演示如何用面向對象的概念編出較少的代碼但更好的程序。祝大家好運。
面向對象編程的概念對每一個作者來說都有不同的看法,我提醒一下一個面向對象語言應有的東西:
- 數據抽象和信息隱藏
- 繼承
- 多態性
在PHP中使用類進行封裝的辦法:
class Something {
// In OOP classes are usually named starting with a cap letter.
var $x;
function setX($v) {
// Methods start in lowercase then use lowercase to seprate
// words in the method name example getValueOfArea()
$this->x=$v;
}
function getX() {
return $this->x;
}
}
?>
當然你可以用你自己的辦法,但有一個標准總是好的。
PHP中類的數據成員使用 "var" 定義,數據成員是沒有類型直到被賦值。一個數據成員可能是一個 integer、數組、聯合數組(associative array)或甚至對象(object). 方法在類裡定義成函數,在方法裡存取數據成員,你必須使用$this->name 這樣的辦法,否則對方法來說是一個函數的局部變量。
使用 new 來創建一個對象
$obj = new Something;
然後使用成員函數
$obj->setX(5);
$see = $obj->getX();
setX 成員函數將 5 賦給對象(而不是類)obj 中成員變量, 然後 getX 返回值 5.
你也可以用對象引用來存取成員變量,例如:$obj->x=6; 然而,這不一種好的面向對象編程的方法。我堅持你應使用成員函數來設置成員變量的值和通過成員函數來讀取成員變量。如果你認為成員變量是不可存取的除了使用成員函數的辦法,你將成為一個好的面向對象程序員。 但不幸的是PHP本身沒有辦法聲明一個變量是私有的,所以允許糟糕的代碼存在。
在 PHP 中繼承使用 extend 來聲明。
class Another extends Something {
var $y;
function setY($v) {
// Methods start in lowercase then use lowercase to seperate
// words in the method name example getValueOfArea()
$this->y=$v;
}
function getY() {
return $this->y;
}
}
?>
這樣類 "Another" 的對象擁有父類的所用成員變量及方法函數,再加上自己的成員變量及成員函數。如:
$obj2=new Another;
$obj2->setX(6);
$obj2->setY(7);
多重繼承不被支持,所以你不能讓一個類繼承多個類。
在繼承類中你可以重新定義來重定義方法,如果我們在 "Another" 重新定義 getX,那麼我們不再能存取 "Something" 中的成員函數 getX. 同樣,如果我們在繼承類中聲明一個和父類同名的成員變量,那麼繼承類的變量將隱藏父類的同名變量。
你可以定義一個類的構造函數, 構造函數是和類同名的成員函數,在你創建類的對象時被調用。
class Something {
var $x;
function Something($y) {
$this->x=$y;
}
function setX($v) {
$this->x=$v;
}
function getX() {
return $this->x;
}
}
?>
所以可以用如下方法創建對象:
$obj=new Something(6);
構造函數自動賦值 5 給成員變量 x, 構造函數和成員函數都是普通的PHP函數,所以你可以使用缺省參數。
function Something($x="3",$y="5")
然後:
$obj=new Something(); // x=3 and y=5
$obj=new Something(8); // x=8 and y=5
$obj=new Something(8,9); // x=8 and y=9
缺省參數的定義方法和 C++ 一樣,因此你不能傳一個值給 Y 但讓 X 取缺省值,實參的傳遞是從左到右,當沒有更多的實參時函數將使用缺省參數。
只有當繼承類的構造函數被調用後,繼承類的對象才被創建,父類的構造函數沒有被調用,這是PHP不同其他面向對象語言的特點,因為構造函數調用鏈是面向對象編程的特點。如果你想調用基類的構造函數,你不得不在繼承類的構造函數中顯式調用它。這樣它能工作是因為在繼承類中父類的方法全部可用。
function Another() {
$this->y=5;
$this->Something(); //explicit call to base class constructor.
}
?>
在面向對象編程中一種好的機制是使用抽象類,抽象類是一種不能實例化而是用來給繼承類定義界面的類。設計師經常使用抽象類來強制程序員只能從特定的基類來繼承,所以就能確定新類有所需的功能,但在PHP中沒有標准的辦法做到這一點,不過:
如果你在定義基類是需要這個特點,可以通過在構造函數中調用 "die",這樣你就可以確保它不能實例化,現在定義抽象類的函數並在每個函數中調用 "die",如果在繼承類中程序員不想重定義而直接調用基類的函數,將會產生一個錯誤。
此外,你需要確信因為PHP沒有類型,有些對象是從基類繼承而來的繼承類創建的,因此增加一個方法在基類來辨別類(返回 "一些標識")並驗證這一點,當你收到一個對象作為參數派上用場。 但對於一個惡棍程序沒用辦法,因為他可以在繼承類中重定義此函數,通常這種辦法只對懶惰的程序員奏效。當然,最好的辦法是防止程序接觸到基類的代碼只提供界面。
重載在PHP中不被支持。在面向對象編程中你可以通過定義不同參數種類和多少來重載一個同名成員函數。PHP是一種松散的類型語言,所以參數類型重載是沒有用的,同樣參數個數不同的辦法重載也不能工作。
有時候,在面向對象編程中重載構造函數很有用,所以你能以不同的方式創建不同的對象(通過傳遞不同的參數個數)。一個小巧門可以做到這一點:
class Myclass {
function Myclass() {
$name="Myclass".func_num_args();
$this->$name();
//Note that $this->$name() is usually wrong but here
//$name is a string with the name of the method to call.
}
function Myclass1($x) {
code;
}
function Myclass2($x,$y) {
code;
}
}
?>
通過這種辦法可以部分達到重載的目的。
$obj1=new Myclass(1); //Will call Myclass1
$obj2=new Myclass(1,2); //Will call Myclass2
感覺還不錯!
[1] [2] 下一頁
多態性
多態性被定義為當在運行時刻一個對象作為參數傳遞時,對象能決定調用那個方法的能力。例如,用一個類定義了方法 "draw",繼承類重定義 "draw" 的行為來畫圓或正方形,這樣你就有一個參數為 x 的函數,在函數裡可以調用$x->draw(). 如果支持多態性,那麼 "draw" 方法的調用就取決於對象 x 的類型。多態性在PHP中很自然被支持(想一想這種情況在C++編譯器中如果編譯,那一個方法被調用?然而你不知道對象的類型是什麼,當然現在不是這種情況)。
幸好PHP支持多態性。
function niceDrawing($x) {
//Supose this is a method of the class Board.
$x->draw();
}
$obj=new Circle(3,187);
$obj2=new Rectangle(4,5);
$board->niceDrawing($obj); //will call the draw method of Circle.
$board->niceDrawing($obj2); //will call the draw method of Rectangle.
?>
PHP的面向對象編程
純對象論者認為PHP不是真正的面向對象語言,這是對的。PHP是一種混合語言,你可以用面向對象或傳統結構編程的方法來使用它。對於大型工程,然而你可能或需要使用純面向對象方法來定義類,並在你的工程中只使用對象和類。越來越大的工程通過使用面向對象的方法會獲得益處,面向對象工程非常容易維持,容易理解並且重用。這是軟件工程的基本。使用這些概念在網站設計中是未來成功的關鍵。
PHP中的高級面向對象技術
在回顧面向對象的基本概念之後,我將介紹一些更高級的技術。
串行化
PHP並不支持持久性對象,在面向對象語言中持久性對象是一些經過應用程序多次調用仍然保持其狀態和功能的對象,這意味著有一種能保存對象到文件或數據庫中然後重新裝載對象。這種機制稱之為串行化。PHP 有一個串行化函數,可以在對象中調用,串行化函數返回一個字符串代表這個對象。然後串行化函數保存的是成員數據而不是成員函數。
在PHP4中,如果你串行化一個對象到字符串 $s, 然後刪除此對象,再反串行化對象到 $obj, 你仍然可以調用對象的方法函數。但我不推薦這種方法,這因為(a)這種功能在將來不一定支持(b)這導致一種幻象,如果你保存串行化對象到磁盤並退出程序。將來重新運行此腳本時你不能反串行化此對象並希望對象的方法函數仍有效,因為串行化