php面向物件基礎詳解【星際爭霸遊戲案例】
本文例項講述了php面向物件基礎。分享給大家供大家參考,具體如下:
前言
面向物件博大精深,對於從未接觸過得的人,會覺得一頭霧水。
學習的資料很多,但大多比較抽象,所以我用經典的遊戲-星際爭霸來討論PHP面向物件。
現在假設我們來用PHP開發星際爭霸,從而接觸PHP面向物件。
注意,為了便於學習,除了特殊說明,否則各部分程式碼之間沒有關聯。而且同一件事情往往用的是不同的程式碼。
另外我也不去考證各個兵種的屬性數字,僅僅用來說明。
一、類和物件
如果玩家制造了一個機槍兵,那麼我們怎麼表示他呢,因為每個機槍兵有幾個基本的資料要記錄:剩餘的血,殺敵數量,攻擊力等等。
我們可以用一個數組來記錄一個機槍兵剩餘的血和殺敵數量,因為這對於每個機槍兵是獨立的。
從這裡我們可以看出一件事情,首先每個機槍兵有獨立的資料需要記錄和修改,比如剩餘的血。同時他們有相同的資料需要共用,比如攻擊力。
這時候面向物件就能幫上我們的忙了。
1.1、類的定義
我們先來處理一部分問題,也就是每個機槍兵獨有的資料。
<?php class marine { public $blood = 50; //剩餘的血 public $kills = 0; //殺敵數量 //這個函式(通常叫做方法)表示攻擊敵人時候的執行程式碼 function attack($enemy) { //攻擊敵人的程式碼 } } ?>
這叫做類,我們建立了一個表示所有機槍兵的類marine,這裡面保留了需要每個兵獨有的資料,比如上面程式碼裡的剩餘的血。
1.2、物件的建立和使用
接下來我們來使用物件,也就是每個機槍兵:
<?php $m1 = new marine(); ?>
通過new後面加一個類的名字和括號,我們新建了一個機槍兵$m1,$m1被叫做類marine的物件,我們可以把它想象成一個特殊變數,只不過裡面儲存了多個數據。
如果需要使用或者操作某個機槍兵的血(物件的屬性),只要用$m1->blood來表示就可以了:echo $m1->blood;//輸出機槍兵$m1剩餘的血
我們再建立一個機槍兵
<?php $m2 = new marine(); ?>
如果此時$m1被敵人攻擊過了,還剩下10個血。而$m2沒受過攻擊:
<?php echo $m1->blood;//結果是10 echo $m2->blood;//結果是50 ?>
使用物件可以很簡單的儲存每個機槍兵的血,不會互相影響。
如果機槍兵$m1攻擊敵人的時候,可以這樣使用物件的方法:
<?php $m1->attack($z1);//假設攻擊的是某個小狗的物件$z1 ?>
不同的類內可以用同名的函式,比如小狗的類Zergling裡面也可以有一個函式attack
要注意的是,從PHP5開始,無論在哪裡改變一個物件的屬性,都能改變它。比如上面一個小狗物件被作為引數傳入機槍兵的attack函式,執行函式之後這個小狗物件的血減少了,這和一般的函式不同。但這是很直觀的,如果一個小狗被攻擊了,它的血就應該減少。
二、建構函式和解構函式
每次我們新建一個機槍兵的時候,總人口應該加1,如果一個機槍兵被殺,人口應該減少1。
可以通過建構函式和解構函式來自動處理:
<?php class marine { //建構函式 function __construct() { //增加總人口的程式碼 } //解構函式 function __destruct() { //減少總人口的程式碼 } } ?>
在一個類中,名字為__construct的函式叫做建構函式,每次new新建一個類的物件的時候就會執行:
<?php $m1 = new marine();//每次製造一個機槍兵時系統會呼叫類marine的建構函式,自動增加總人口 ?>
在一個類中,名字為__destruct的函式叫做解構函式,每次銷燬一個類的物件的時候就會執行:
<?php unset($m1);//unset可以用於物件,表示銷燬一個物件。每次一個機槍兵被殺時系統會呼叫類marine的解構函式,自動減少總人口 ?>
三、靜態
機槍兵的攻擊力是屬於所有機槍兵物件,每個機槍兵的攻擊力都是一樣的,如果升級,應該一起變化。
這就用到static,表示靜態:
<?php class marine { static $attackNumber = 10; //攻擊力的數字 //這個函式表示攻擊敵人時候的執行程式碼 function attack($enemy) { //攻擊敵人的程式碼,$enemy->blood表示敵人物件的血屬性 $enemy->blood -= self::$attackNumber; } } ?>
靜態屬性表示類所有的物件都共享的屬性,一旦改變,所有的物件都跟著變化。
靜態屬性用static開頭,比如上面的static $attackNumber。
靜態屬性可以用類直接訪問:
<?php echo marine::$attackNumber;//顯示10 ?>
如果類以內的函式訪問,用self::$attackNumber表示本類的$attackNumber屬性
所以如果我們升級了機槍兵的攻擊力,所有的機槍兵都受影響,這就是面向物件的好處之一,也解決了我們前面討論的共同資料的問題。
函式也可以是靜態的,這樣就可以用類直接訪問,不需要新建物件來呼叫:
<?php class marine { static $attackNumber = 10; //攻擊力的數字 //這個函式表示機槍兵升級的執行程式碼 staticfunction upgrade() { self::$attacknum++; } } ?>
如果科技建築升級完畢,直接就呼叫這個函式:
<?php marine::upgrade(); ?>
四、繼承
兵營用來造機槍兵,坦克房用來製造坦克,他們都是建築,但是卻有很多不同,如果用一個類“建築”來表示,很困難。
但我們要保留他們的共性,比如都能飛行,不希望飛行的程式碼在各個類重複寫,又要讓他們能各自獨立的生產不同的東西。
所以我們可以用繼承來處理,繼承表示父子關係,被繼承的叫父類,繼承的叫子類。用extends表示繼承
<?php //建築類 class building { function fly() { //建築飛行的程式碼 } } //兵營類 class marineBuilding extends building { function createMarine() { //製造機槍兵的程式碼 } } //坦克房類 class tankBuilding extends building { function createTank() { //製造坦克的程式碼 } } ?>
接下來,我們看看繼承產生的效果:
<?php //如果造了一個兵營: $mb1 = new marineBuilding(); //一旦他需要飛行,就可以直接使用建築類的函式fly(),儘管兵營類的定義裡沒有這個函式 $mb1->fly(); //而他要製造機槍兵的時候: $mb1->createMarine(); ?>
同樣是繼承建築類的坦克房類,就無法制造機槍兵,因為這是兵營類的個性。
如果在子類中的函式呼叫父類的函式,要使用parent,比如parent::fly()
注意,一個類只能有一個父類,PHP不允許多重繼承,也就是說一個孩子只能有一個爹,一個爹可以有N個孩子!
五、訪問控制
如果用$attackNumber = 10表示屬性的話,系統預設是public $attackNumber = 10,所以建議這樣寫:
<?php class marine { public static $attackNumber = 10; //攻擊力的數字 } ?>
public表示這個屬性是公共的,也就是在任何地方都可以訪問和操作的。
但這就存在一些問題,如果有玩家知道了類marine的一些程式碼結構,那他做個簡單的補丁程式,執行的時候載入上去:
<?php //補丁 marine::$attackNumber = 10000; ?>
這樣的話,他的機槍兵有10000的攻擊力,呵呵,這樣的話,誰打得過他!
為此我們要用private,表示這個屬性只有類裡面的函式才能訪問:
<?php class marine { private static $attackNumber = 10; //攻擊力的數字 //這個函式表示機槍兵升級的執行程式碼 function upgrade() { //這樣防止無限升級 if(self::$attacknum<13) { self::$attacknum++; } } } ?>
這樣一來,只有升級才能改變機槍兵的攻擊力。
但是現在往往是團隊開發,而且很多用到類的繼承,如果private的話,子類就無法訪問了,但又不希望隨便都可以修改某些屬性。
那麼可以用protected,protected的屬性可以被子類的函式訪問。
六、過載
6.1、屬性過載
如果我們把地面部隊作為一個類,讓機槍兵類來繼承他,這時候如果地面部隊類和機槍兵類裡面都定義了攻擊力$attackNumber,那麼每個兵的攻擊力就決定於機槍兵類,而不是地面部隊。這就叫做過載。
<?php //地面部隊 class groundArmy { public $attackNumber = 5; } //機槍兵 class marine extends groundArmy { public $attackNumber = 10; //攻擊力的數字 } $m1 = new marine();//新建一個機槍兵 echo $m1->attackNumber;//顯示攻擊力為10 ?>
6.2、函式過載
過載也可以用於函式,子類的函式如果和父類函式同名,除非另行說明,否則子類的物件預設呼叫子類內的函式。
比如人族的鬼兵類ghost和神族類的黑暗聖堂類(隱刀),都是隱形兵種,但是鬼兵隱形的時候會減少能量,黑暗聖堂根本沒有能量屬性。
如果我們把隱形能力作為父類,鬼兵類ghost和神族類的黑暗聖堂類DarkTemplar來繼承它,同時實現不同的隱形程式碼:
<?php //隱形能力類 class concealAbility { //這個函式表示隱形的執行程式碼 function conceal() { //隱形的執行程式碼 } } //鬼兵類 class ghost extends concealAbility { $energy = 150; //這個函式表示隱形的執行程式碼 function conceal() { //隱形的執行程式碼 //減少鬼兵的能量,$this表示當前物件,也就是當前這個鬼兵 $this->energy -= 25; } } //黑暗聖堂類 class DarkTemplar extends concealAbility { //這個函式表示隱形的執行程式碼 function conceal() { //隱形的執行程式碼,不影響能量 } } //新建一個鬼兵 $g1 = new ghost(); //顯示能量為150 echo $g1->energy; //鬼兵隱形 $g1->conceal(); //顯示能量為125 echo $g1->energy; //新建一個黑暗聖堂 $d1 = new DarkTemplar(); //黑暗聖堂隱形,他沒有能量屬性 $g1->conceal(); ?>
七、介面
PHP不允許多重繼承,那麼有些問題就難辦了。
假如為了規範處理,我們把隱形的能力建立一個類,然後把飛行能力放一個類,那麼人族的偵察機怎麼處理?不能繼承兩個類!
那我們不用繼承也行,但是開發組的其他人一旦涉及到偵察機,要把長長的程式碼讀一遍嗎?有沒有可能知道類的所有方法的簡要描述?
可以用到介面interface,一個類可以執行(繼承)多個介面,介面中定義的函式不能有函式體,執行介面的類必須將這些函式完整定義。
這樣我們知道偵察機實現了飛行能力介面,必然有接口裡面描述的飛行方法://隱形能力的介面
<?php interface concealAbility { public function conceal(); } //飛行能力的介面 interface flyAbility { public function fly(); } //偵察機類 class Wraith implements flyAbility,concealAbility { //這個函式表示偵察機飛行的執行程式碼 function fly() { //飛行的執行程式碼 } //這個函式表示偵察機隱形的執行程式碼 function conceal() { //隱形的執行程式碼 } } ?>
八、總結
我們討論了PHP面向物件的基本知識,通過星際爭霸這一經典的遊戲來說明,大家可以看到面向物件的初步作用。
我們看到通過面向物件可以使程式碼更加清晰,類將程式碼組織起來,比較方便的重複使用。
同時物件也減少了變數的衝突,方便相關性資料的儲存和使用。
如果要解決的問題涉及很多方面,面向物件可以演化出更加靈活和有技巧的方式,比如通常提到的設計模式,和很多框架。
當然,面向物件也有缺點,從上面的程式碼可以看到,首先程式碼就多了,簡單的任務如果定義許多類,反而麻煩。
對於簡單任務,面向物件也可能使程式碼執行的效率降低。
深入的探討,超出了本文的範圍。
更多關於PHP相關內容感興趣的讀者可檢視本站專題:《php面向物件程式設計入門教程》、《PHP陣列(Array)操作技巧大全》、《PHP基本語法入門教程》、《PHP運算與運算子用法總結》、《php字串(string)用法總結》、《php+mysql資料庫操作入門教程》及《php常見資料庫操作技巧彙總》
希望本文所述對大家PHP程式設計有所幫助。