PHP中實現物件導向編程(下)
來源:互聯網
上載者:User
編程|對象 多態性
多態性被定義為當在運行時刻一個對象作為參數傳遞時,對象能決定調用那個方法的能力。例如,用一個類定義了方法 "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)這導致一種幻象,如果你儲存序列化對象到磁碟並退出程式。將來重新運行此指令碼時你不能反序列化此對象並希望對象的方法函數仍有效,因為序列化出來的字串並沒有表示任何成員函數。最後,序列化儲存對象的成員變數在PHP中非常有用,僅僅如此. (你可以序列化聯合數組和數組到磁碟裡)。
例子:
$obj=new Classfoo();
$str=serialize($obj);
// Save $str to disk
//...some months later
//Load str from disk
$obj2=unserialize($str)
?>
上例中,你可以恢複成員變數而沒有成員函數(根據文檔)。這導致 $obj2->x 是唯一的方法來存取成員變數(因為沒有成員函數)。
這裡還有一些方法解決這個問題,但我留下給你因為它會搞髒這個乾淨的文檔。
我希望PHP將來能全面支援序列化。
使用類來操縱儲存的資料
PHP和物件導向編程中一個比較好的地方是你很容易定義類來操縱某些東西,並且當需要時調用合適的類。假設有一個HTML檔案,你需要通過選擇產品的ID號來選擇一個產品,你的資料儲存在資料庫中,而你想顯示產品的資訊,如價格等等。你有不同種類的產品,同樣的動作對不同的產品有不同的含義。
例如,顯示一個聲音意味著播放它,而對其他產品來說可能是顯示一個儲存在資料庫的圖片。你可以用物件導向編程和PHP來達到,代碼少但更好。
定義一個類,定義類應該有的方法,然後通過繼承來定義每一種產品的類(SoundItem類, ViewableItem類,等等),重定義每個產品類的方法,使它們如你所需。根據你儲存在資料庫中的表的產品類型欄位來給每一種產品類型定義一個類,一個典型的產品表應有欄位(id, 類型, 價格, 描述,等等)。
在指令碼中你從資料庫的表中擷取類型資訊,然後執行個體化相應類的對象:
$obj=new $type();
$obj->action();
?>
這是PHP比較的特性,你可以調用 $obj 的顯示方法或其他方法而不用去管對象的類型。通過這種技術,當你增加一種新類型的對象時,你不需要去修改指令碼。這個方法有點威力,就是定義所有對象應有的方法而不管它的類型,然後在不同的類中以不同的方式來實現,這樣就可以在指令碼中對不同的類型對象使用他們,再沒有 if, 沒有兩個程式員在同一個檔案裡,永遠快樂。你相信編程是這樣快樂不?維護代價小並且可重用?
如果你帶領一組程式員,最好的方法是劃分任務,每人可以對某種類和對象負責。國際化可以用同樣的技術解決,使合適的類對應使用者選擇的不同的語言等等。
複製和複製
當你建立一個對象 $obj, 你可以使用 $obj2 = $obj 來拷貝一個對象,新的對象是 $obj 的一個拷貝(不是引用),所以在賦值完新對象有 $obj 同新的狀態。有時候你不想這樣,只想建立和 obj 同樣的新對象,調用新對象的建構函式如同你曾使用過 new 命令。這可以通過PHP的序列化和使用基類並且其他類必須從基類繼承來達到。
進行危險的地帶
當你序列化一個對象,你得到一個有特定格式的字串,如果你有好奇心,可能你會探尋其中的秘密,字串中有一個東西就是類的名字,你可以解開它:
$herring=serialize($obj);
$vec=explode(:,$herring);
$nam=str_replace("\"",\,$vec[2]);
?>
假設你建立一個類 "Universe" 並且使所有類都從 "Universe" 繼承而來,你可以在 "Universe" 定義一個複製的方法:
class Universe {
function clone() {
$herring=serialize($this);
$vec=explode(:,$herring);
$nam=str_replace("\"",\,$vec[2]);
$ret=new $nam;
return $ret;
}
}
//Then:
$obj=new Something();
//Something extends Universe !!
$other=$obj->clone();
?>
你所得的是類 Something 的新對象如同使用 new 一樣,並且建構函式被調用等等。我不知道這對你是不是有用,這是一個很好的實踐,Universe 類知道它的繼承類的名字。對你來說,唯一的限制是你的想象力!!!
注意:我使用的是PHP4, 文章裡有些東西可能不適合PHP3。