PHP中完成面向?qū)ο缶幊?/h1>
發(fā)表時間:2024-02-07 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]這篇文章介紹在PHP的面向?qū)ο缶幊蹋∣OP)。我將演示如何用面向?qū)ο蟮母拍罹幊鲚^少的代碼但更好的程序。祝大家好運。 面向?qū)ο缶幊痰母拍顚γ恳粋作者來說都有不同的看法,我提醒一下一個面向?qū)ο笳Z言應(yīng)有的東西: - 數(shù)據(jù)抽象和信息隱藏 - 繼承 - 多態(tài)性 在PHP中使用類進(jìn)行...
這篇文章介紹在PHP的面向?qū)ο缶幊蹋∣OP)。我將演示如何用面向?qū)ο蟮母拍罹幊鲚^少的代碼但更好的程序。祝大家好運。
面向?qū)ο缶幊痰母拍顚γ恳粋作者來說都有不同的看法,我提醒一下一個面向?qū)ο笳Z言應(yīng)有的東西:
- 數(shù)據(jù)抽象和信息隱藏
- 繼承
- 多態(tài)性
在PHP中使用類進(jìn)行封裝的辦法:
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; } }
?> |
當(dāng)然你可以用你自己的辦法,但有一個標(biāo)準(zhǔn)總是好的。
PHP中類的數(shù)據(jù)成員使用 "var" 定義,數(shù)據(jù)成員是沒有類型直到被賦值。一個數(shù)據(jù)成員可能是一個 integer、數(shù)組、聯(lián)合數(shù)組(associative array)或甚至對象(object). 方法在類里定義成函數(shù),在方法里存取數(shù)據(jù)成員,你必須使用$this->name 這樣的辦法,否則對方法來說是一個函數(shù)的局部變量。
使用 new 來創(chuàng)建一個對象
然后使用成員函數(shù)
$obj->setX(5); $see = $obj->getX(); |
setX 成員函數(shù)將 5 賦給對象(而不是類)obj 中成員變量, 然后 getX 返回值 5.
你也可以用對象引用來存取成員變量,例如:$obj->x=6; 然而,這不一種好的面向?qū)ο缶幊痰姆椒āN覉猿帜銘?yīng)使用成員函數(shù)來設(shè)置成員變量的值和通過成員函數(shù)來讀取成員變量。如果你認(rèn)為成員變量是不可存取的除了使用成員函數(shù)的辦法,你將成為一個好的面向?qū)ο蟪绦騿T。 但不幸的是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" 的對象擁有父類的所用成員變量及方法函數(shù),再加上自己的成員變量及成員函數(shù)。如:
$obj2=new Another; $obj2->setX(6); $obj2->setY(7); |
多重繼承不被支持,所以你不能讓一個類繼承多個類。
在繼承類中你可以重新定義來重定義方法,如果我們在 "Another" 重新定義 getX,那么我們不再能存取 "Something" 中的成員函數(shù) getX. 同樣,如果我們在繼承類中聲明一個和父類同名的成員變量,那么繼承類的變量將隱藏父類的同名變量。
你可以定義一個類的構(gòu)造函數(shù), 構(gòu)造函數(shù)是和類同名的成員函數(shù),在你創(chuàng)建類的對象時被調(diào)用。
class Something { var $x;
function Something($y) { $this->x=$y; }
function setX($v) { $this->x=$v; }
function getX() { return $this->x; } }
?> |
[page_break] 所以可以用如下方法創(chuàng)建對象:
構(gòu)造函數(shù)自動賦值 5 給成員變量 x, 構(gòu)造函數(shù)和成員函數(shù)都是普通的PHP函數(shù),所以你可以使用缺省參數(shù)。
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 |
缺省參數(shù)的定義方法和 C++ 一樣,因此你不能傳一個值給 Y 但讓 X 取缺省值,實參的傳遞是從左到右,當(dāng)沒有更多的實參時函數(shù)將使用缺省參數(shù)。
只有當(dāng)繼承類的構(gòu)造函數(shù)被調(diào)用后,繼承類的對象才被創(chuàng)建,父類的構(gòu)造函數(shù)沒有被調(diào)用,這是PHP不同其他面向?qū)ο笳Z言的特點,因為構(gòu)造函數(shù)調(diào)用鏈?zhǔn)敲嫦驅(qū)ο缶幊痰奶攸c。如果你想調(diào)用基類的構(gòu)造函數(shù),你不得不在繼承類的構(gòu)造函數(shù)中顯式調(diào)用它。這樣它能工作是因為在繼承類中父類的方法全部可用。
function Another() { $this->y=5; $this->Something(); //explicit call to base class constructor. }
?> |
在面向?qū)ο缶幊讨幸环N好的機(jī)制是使用抽象類,抽象類是一種不能實例化而是用來給繼承類定義界面的類。設(shè)計師經(jīng)常使用抽象類來強(qiáng)制程序員只能從特定的基類來繼承,所以就能確定新類有所需的功能,但在PHP中沒有標(biāo)準(zhǔn)的辦法做到這一點,不過:
如果你在定義基類是需要這個特點,可以通過在構(gòu)造函數(shù)中調(diào)用 "die",這樣你就可以確保它不能實例化,現(xiàn)在定義抽象類的函數(shù)并在每個函數(shù)中調(diào)用 "die",如果在繼承類中程序員不想重定義而直接調(diào)用基類的函數(shù),將會產(chǎn)生一個錯誤。
此外,你需要確信因為PHP沒有類型,有些對象是從基類繼承而來的繼承類創(chuàng)建的,因此增加一個方法在基類來辨別類(返回 "一些標(biāo)識")并驗證這一點,當(dāng)你收到一個對象作為參數(shù)派上用場。 但對于一個惡棍程序沒用辦法,因為他可以在繼承類中重定義此函數(shù),通常這種辦法只對懶惰的程序員奏效。當(dāng)然,最好的辦法是防止程序接觸到基類的代碼只提供界面。
重載在PHP中不被支持。在面向?qū)ο缶幊讨心憧梢酝ㄟ^定義不同參數(shù)種類和多少來重載一個同名成員函數(shù)。PHP是一種松散的類型語言,所以參數(shù)類型重載是沒有用的,同樣參數(shù)個數(shù)不同的辦法重載也不能工作。
有時候,在面向?qū)ο缶幊讨兄剌d構(gòu)造函數(shù)很有用,所以你能以不同的方式創(chuàng)建不同的對象(通過傳遞不同的參數(shù)個數(shù))。一個小巧門可以做到這一點:
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; } }
?> |
通過這種辦法可以部分達(dá)到重載的目的。
$obj1=new Myclass(1); //Will call Myclass1 $obj2=new Myclass(1,2); //Will call Myclass2 |
[page_break]
多態(tài)性
多態(tài)性被定義為當(dāng)在運行時刻一個對象作為參數(shù)傳遞時,對象能決定調(diào)用那個方法的能力。例如,用一個類定義了方法 "draw",繼承類重定義 "draw" 的行為來畫圓或正方形,這樣你就有一個參數(shù)為 x 的函數(shù),在函數(shù)里可以調(diào)用$x->draw(). 如果支持多態(tài)性,那么 "draw" 方法的調(diào)用就取決于對象 x 的類型。多態(tài)性在PHP中很自然被支持(想一想這種情況在C++編譯器中如果編譯,那一個方法被調(diào)用?然而你不知道對象的類型是什么,當(dāng)然現(xiàn)在不是這種情況)。
幸好PHP支持多態(tài)性。
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的面向?qū)ο缶幊?
純對象論者認(rèn)為PHP不是真正的面向?qū)ο笳Z言,這是對的。PHP是一種混合語言,你可以用面向?qū)ο蠡騻鹘y(tǒng)結(jié)構(gòu)編程的方法來使用它。對于大型工程,然而你可能或需要使用純面向?qū)ο蠓椒▉矶x類,并在你的工程中只使用對象和類。越來越大的工程通過使用面向?qū)ο蟮姆椒〞@得益處,面向?qū)ο蠊こ谭浅H菀拙S持,容易理解并且重用。這是軟件工程的基本。使用這些概念在網(wǎng)站設(shè)計中是未來成功的關(guān)鍵。
PHP中的高級面向?qū)ο蠹夹g(shù)
在回顧面向?qū)ο蟮幕靖拍钪,我將介紹一些更高級的技術(shù)。
串行化
PHP并不支持持久性對象,在面向?qū)ο笳Z言中持久性對象是一些經(jīng)過應(yīng)用程序多次調(diào)用仍然保持其狀態(tài)和功能的對象,這意味著有一種能保存對象到文件或數(shù)據(jù)庫中然后重新裝載對象。這種機(jī)制稱之為串行化。PHP 有一個串行化函數(shù),可以在對象中調(diào)用,串行化函數(shù)返回一個字符串代表這個對象。然后串行化函數(shù)保存的是成員數(shù)據(jù)而不是成員函數(shù)。
在PHP4中,如果你串行化一個對象到字符串 $s, 然后刪除此對象,再反串行化對象到 $obj, 你仍然可以調(diào)用對象的方法函數(shù)。但我不推薦這種方法,這因為(a)這種功能在將來不一定支持(b)這導(dǎo)致一種幻象,如果你保存串行化對象到磁盤并退出程序。將來重新運行此腳本時你不能反串行化此對象并希望對象的方法函數(shù)仍有效,因為串行化出來的字符串并沒有表示任何成員函數(shù)。最后,串行化保存對象的成員變量在PHP中非常有用,僅僅如此. (你可以串行化聯(lián)合數(shù)組和數(shù)組到磁盤里)。
例子:
$obj=new Classfoo(); $str=serialize($obj); // Save $str to disk
//...some months later
//Load str from disk $obj2=unserialize($str)
?> |
上例中,你可以恢復(fù)成員變量而沒有成員函數(shù)(根據(jù)文檔)。這導(dǎo)致 $obj2->x 是唯一的方法來存取成員變量(因為沒有成員函數(shù))。
這里還有一些方法解決這個問題,但我留下給你因為它會搞臟這個干凈的文檔。
我希望PHP將來能全面支持串行化。
使用類來操縱保存的數(shù)據(jù)
PHP和面向?qū)ο缶幊讨幸粋比較好的地方是你很容易定義類來操縱某些東西,并且當(dāng)需要時調(diào)用合適的類。假設(shè)有一個HTML文件,你需要通過選擇產(chǎn)品的ID號來選擇一個產(chǎn)品,你的數(shù)據(jù)保存在數(shù)據(jù)庫中,而你想顯示產(chǎn)品的信息,如價格等等。你有不同種類的產(chǎn)品,同樣的動作對不同的產(chǎn)品有不同的含義。
例如,顯示一個聲音意味著播放它,而對其他產(chǎn)品來說可能是顯示一個存儲在數(shù)據(jù)庫的圖片。你可以用面向?qū)ο缶幊毯蚉HP來達(dá)到,代碼少但更好。
[page_break] 定義一個類,定義類應(yīng)該有的方法,然后通過繼承來定義每一種產(chǎn)品的類(SoundItem類, ViewableItem類,等等),重定義每個產(chǎn)品類的方法,使它們?nèi)缒闼。根?jù)你保存在數(shù)據(jù)庫中的表的產(chǎn)品類型字段來給每一種產(chǎn)品類型定義一個類,一個典型的產(chǎn)品表應(yīng)有字段(id, 類型, 價格, 描述,等等)。
在腳本中你從數(shù)據(jù)庫的表中獲取類型信息,然后實例化相應(yīng)類的對象:
$obj=new $type(); $obj->action();
?> |
這是PHP比較的特性,你可以調(diào)用 $obj 的顯示方法或其他方法而不用去管對象的類型。通過這種技術(shù),當(dāng)你增加一種新類型的對象時,你不需要去修改腳本。這個方法有點威力,就是定義所有對象應(yīng)有的方法而不管它的類型,然后在不同的類中以不同的方式來實現(xiàn),這樣就可以在腳本中對不同的類型對象使用他們,再沒有 if, 沒有兩個程序員在同一個文件里,永遠(yuǎn)快樂。你相信編程是這樣快樂不?維護(hù)代價小并且可重用?
如果你帶領(lǐng)一組程序員,最好的方法是劃分任務(wù),每人可以對某種類和對象負(fù)責(zé)。國際化可以用同樣的技術(shù)解決,使合適的類對應(yīng)使用者選擇的不同的語言等等。
復(fù)制和克隆 當(dāng)你創(chuàng)建一個對象 $obj, 你可以使用 $obj2 = $obj 來拷貝一個對象,新的對象是 $obj 的一個拷貝(不是引用),所以在賦值完新對象有 $obj 同新的狀態(tài)。有時候你不想這樣,只想創(chuàng)建和 obj 同樣的新對象,調(diào)用新對象的構(gòu)造函數(shù)如同你曾使用過 new 命令。這可以通過PHP的串行化和使用基類并且其他類必須從基類繼承來達(dá)到。
進(jìn)行危險的地帶 當(dāng)你串行化一個對象,你得到一個有特定格式的字符串,如果你有好奇心,可能你會探尋其中的秘密,字符串中有一個東西就是類的名字,你可以解開它:
$herring=serialize($obj); $vec=explode(:,$herring); $nam=str_replace("\"",\,$vec[2]);
?> |
假設(shè)你創(chuàng)建一個類 "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 一樣,并且構(gòu)造函數(shù)被調(diào)用等等。我不知道這對你是不是有用,這是一個很好的實踐,Universe 類知道它的繼承類的名字。對你來說,唯一的限制是你的想象力。!
注意:我使用的是PHP4, 文章里有些東西可能不適合PHP3。