用星際爭霸講解PHP面向?qū)ο蟮母拍?/h1>
在學習PHP的時候,感覺自己對面向?qū)ο罄斫膺€不深刻,很多時候是一頭霧水。通過別人的推薦,找到了這篇通過星際爭霸來講解面向?qū)ο蟾拍畹奈恼?,轉(zhuǎn)載出來供有需要的朋友學習。
一、類和對象
如果玩家制造了一個機槍兵,那么我們怎么表示他呢,因為每個機槍兵有幾個基本的數(shù)據(jù)要記錄:剩余的血,殺敵數(shù)量,攻擊力等等。我們可以用一個數(shù)組來記錄一個機槍兵剩余的血和殺敵數(shù)量,因為這對于每個機槍兵是獨立的。但攻擊力比較麻煩,因為經(jīng)過升級,攻擊力會增加,這就必須要找出所有表示機槍兵的數(shù)組,然后進行修改,非常麻煩。從這里我們可以看出一件事情,首先每個機槍兵有獨立的數(shù)據(jù)需要記錄和修改,比如剩余的血。同時他們有相同的數(shù)據(jù)需要共用,比如攻擊力。這時候面向?qū)ο缶湍軒蜕衔覀兊拿α恕?/p>
1.1、類的定義
我們先來處理一部分問題,也就是每個機槍兵獨有的數(shù)據(jù)。
- class marine
- {
- public $blood = 50; //剩余的血
- public $kills = 0; //殺敵數(shù)量
- //這個函數(shù)(通常叫做方法)表示攻擊敵人時候的運行代碼 function attack($enemy)
- {
- //攻擊敵人的代碼
- }
- }
這叫做類,我們建立了一個表示所有機槍兵的類marine,這里面保留了需要每個兵獨有的數(shù)據(jù),比如上面代碼里的剩余的血。
1.2、對象的創(chuàng)建和使用
接下來我們來使用對象,也就是每個機槍兵:
- $m1 = new marine();
通過new后面加一個類的名字和括號,我們新建了一個機槍兵$m1,$m1被叫做類marine的對象,我們可以把它想象成一個特殊變量,只不過里面保存了多個數(shù)據(jù)。如果需要使用或者操作某個機槍兵的血(對象的屬性),只要用$m1->blood來表示就可以了:
- echo $m1->blood;//輸出機槍兵$m1剩余的血
我們再建立一個機槍兵
- $m2 = new marine();
如果此時$m1被敵人攻擊過了,還剩下10個血。而$m2沒受過攻擊:
- echo $m1->blood;//結(jié)果是10
- echo $m2->blood;//結(jié)果是50
使用對象可以很簡單的保存每個機槍兵的血,不會互相影響。如果機槍兵$m1攻擊敵人的時候,可以這樣使用對象的方法:
- $m1->attack($z1);//假設(shè)攻擊的是某個小狗的對象$z1
不同的類內(nèi)可以用同名的函數(shù),比如小狗的類Zergling里面也可以有一個函數(shù)attack。要注意的是,從PHP5開始,無論在哪里改變一個對象的屬性,都能改變它。比如上面一個小狗對象被作為參數(shù)傳入機槍兵的attack函數(shù),執(zhí)行函數(shù)之后這個小狗對象的血減少了,這和一般的函數(shù)不同。但這是很直觀的,如果一個小狗被攻擊了,它的血就應(yīng)該減少。
二、構(gòu)造函數(shù)和析構(gòu)函數(shù)
每次我們新建一個機槍兵的時候,總?cè)丝趹?yīng)該加1,如果一個機槍兵被殺,人口應(yīng)該減少1??梢酝ㄟ^構(gòu)造函數(shù)和析構(gòu)函數(shù)來自動處理:
- class marine
- {
- //構(gòu)造函數(shù)
- function __construct()
- {
- //增加總?cè)丝诘拇a
- }
- //析構(gòu)函數(shù)
- function __destruct()
- {
- //減少總?cè)丝诘拇a
- }
- }
在一個類中,名字為__construct的函數(shù)叫做構(gòu)造函數(shù),每次new新建一個類的對象的時候就會執(zhí)行:
- $m1 = new marine();//每次制造一個機槍兵時系統(tǒng)會調(diào)用類marine的構(gòu)造函數(shù),自動增加總?cè)丝?/span>
在一個類中,名字為__destruct的函數(shù)叫做析構(gòu)函數(shù),每次銷毀一個類的對象的時候就會執(zhí)行:
- unset($m1);//unset可以用于對象,表示銷毀一個對象。每次一個機槍兵被殺時系統(tǒng)會調(diào)用類marine的析構(gòu)函數(shù),自動減少總?cè)丝?/span>
三、靜態(tài)
機槍兵的攻擊力是屬于所有機槍兵對象,每個機槍兵的攻擊力都是一樣的,如果升級,應(yīng)該一起變化。這就用到static,表示靜態(tài):
- class marine
- {
- static $attackNumber = 10; //攻擊力的數(shù)字
- //這個函數(shù)表示攻擊敵人時候的運行代碼 function attack($enemy)
- {
- //攻擊敵人的代碼,$enemy->blood表示敵人對象的血屬性
- $enemy->blood -= self::$attackNumber;
- }
- }
靜態(tài)屬性表示類所有的對象都共享的屬性,一旦改變,所有的對象都跟著變化。靜態(tài)屬性用static開頭,比如上面的static $attackNumber。靜態(tài)屬性可以用類直接訪問:
- echo marine::$attackNumber;//顯示10
如果類以內(nèi)的函數(shù)訪問,用self::$attackNumber表示本類的$attackNumber屬性。所以如果我們升級了機槍兵的攻擊力,所有的 機槍兵都受影響,這就是面向?qū)ο蟮暮锰幹?,也解決了我們前面討論的共同數(shù)據(jù)的問題。函數(shù)也可以是靜態(tài)的,這樣就可以用類直接訪問,不需要新建對象來調(diào) 用:
- class marine
- {
- static $attackNumber = 10; //攻擊力的數(shù)字
- //這個函數(shù)表示機槍兵升級的運行代碼 static function upgrade()
- {
- self::$attacknum++;
- }
- }
#p#
如果科技建筑升級完畢,直接就調(diào)用這個函數(shù):
marine::upgrade();
- //建筑類
- class building
- {
- function fly()
- {
- //建筑飛行的代碼
- }
- }
- //兵營類
- class marineBuilding extends building
- {
- function createMarine()
- {
- //制造機槍兵的代碼
- }
- }
- //坦克房類
- class tankBuilding extends building
- {
- function createTank()
- {
- //制造坦克的代碼
- }
- }
- 接下來,我們看看繼承產(chǎn)生的效果:
- ?
- //如果造了一個兵營:
- $mb1 = new marineBuilding();
- /**
- 一旦他需要飛行,就可以直接使用建筑類的函數(shù)fly(),盡管兵營類的定義里沒有這個函數(shù)
- */
- $mb1->fly();
- //而他要制造機槍兵的時候:
- $mb1->createMarine();
同樣是繼承建筑類的坦克房類,就無法制造機槍兵,因為這是兵營類的個性。如果在子類中的函數(shù)調(diào)用父類的函數(shù),要使用parent,比如parent::fly()。注意,一個類只能有一個父類,PHP不允許多重繼承,也就是說一個孩子只能有一個爹,一個爹可以有N個孩子!
五、訪問控制
如果用$attackNumber = 10表示屬性的話,系統(tǒng)默認是public $attackNumber = 10,所以建議這樣寫:
- class marine
- {
- public static $attackNumber = 10; //攻擊力的數(shù)字
- }
public表示這個屬性是公共的,也就是在任何地方都可以訪問和操作的。但這就存在一些問題,如果有玩家知道了類marine的一些代碼結(jié)構(gòu),那他做個簡單的補丁程序,運行的時候加載上去:
- //補丁
- marine::$attackNumber = 10000;
這樣的話,他的機槍兵有10000的攻擊力,呵呵,這樣的話,誰打得過他!為此我們要用private,表示這個屬性只有類里面的函數(shù)才能訪問:
- class marine
- {
- private static $attackNumber = 10; //攻擊力的數(shù)字
- //這個函數(shù)表示機槍兵升級的運行代碼 function upgrade()
- {
- //這樣防止無限升級
- if(self::$attacknum<13)
- {
- self::$attacknum++;
- }
- }
- }
這樣一來,只有升級才能改變機槍兵的攻擊力。但是現(xiàn)在往往是團隊開發(fā),而且很多用到類的繼承,如果private的話,子類就無法訪問了,但又不希望隨便都可以修改某些屬性。那么可以用protected,protected的屬性可以被子類的函數(shù)訪問。
六、重載
6.1、屬性重載
如果我們把地面部隊作為一個類,讓機槍兵類來繼承他,這時候如果地面部隊類和機槍兵類里面都定義了攻擊力$attackNumber,那么每個兵的攻擊力就決定于機槍兵類,而不是地面部隊。這就叫做重載。
- //地面部隊
- class groundArmy
- {
- public $attackNumber = 5;
- }
- //機槍兵
- class marine extends groundArmy
- {
- public $attackNumber = 10; //攻擊力的數(shù)字
- }
- $m1 = new marine();//新建一個機槍兵
- echo $m1->attackNumber;//顯示攻擊力為10
6.2、函數(shù)重載
重載也可以用于函數(shù),子類的函數(shù)如果和父類函數(shù)同名,除非另行說明,否則子類的對象默認調(diào)用子類內(nèi)的函數(shù)。比如人族的鬼兵類ghost和神族類的黑 暗圣堂類(隱刀),都是隱形兵種,但是鬼兵隱形的時候會減少能量,黑暗圣堂根本沒有能量屬性。如果我們把隱形能力作為父類,鬼兵類ghost和神族類的黑 暗圣堂類DarkTemplar來繼承它,同時實現(xiàn)不同的隱形代碼:
#p#
- //隱形能力類
- class concealAbility
- {
- //這個函數(shù)表示隱形的運行代碼 function conceal()
- {
- //隱形的運行代碼
- }
- }
- //鬼兵類
- class ghost extends concealAbility
- {
- $energy = 150;
- //這個函數(shù)表示隱形的運行代碼 function conceal()
- {
- //隱形的運行代碼
- //減少鬼兵的能量,$this表示當前對象,也就是當前這個鬼兵
- $this->energy -= 25;
- }
- }
- //黑暗圣堂類
- class DarkTemplar extends concealAbility
- {
- //這個函數(shù)表示隱形的運行代碼 function conceal()
- {
- //隱形的運行代碼,不影響能量
- }
- }
- //新建一個鬼兵
- $g1 = new ghost();
- //顯示能量為150
- echo $g1->energy;
- //鬼兵隱形
- $g1->conceal();
- //顯示能量為125
- echo $g1->energy;
- //新建一個黑暗圣堂
- $d1 = new DarkTemplar();
- //黑暗圣堂隱形,他沒有能量屬性
- $g1->conceal();
七、接口
PHP不允許多重繼承,那么有些問題就難辦了。假如為了規(guī)范處理,我們把隱形的能力建立一個類,然后把飛行能力放一個類,那么人族的偵察機怎么處 理?不能繼承兩個類!那我們不用繼承也行,但是開發(fā)組的其他人一旦涉及到偵察機,要把長長的代碼讀一遍嗎?有沒有可能知道類的所有方法的簡要描述?可以用 到接口interface
,一個類可以執(zhí)行(繼承)多個接口,接口中定義的函數(shù)不能有函數(shù)體,執(zhí)行接口的類必須將這些函數(shù)完整定義。這樣我們知道偵察機實現(xiàn)了飛行能力接口,必然有接口里面描述的飛行方法:
- //隱形能力的接口
- interface concealAbility
- {
- public function conceal();
- }
- //飛行能力的接口
- interface flyAbility
- {
- public function fly();
- }
- //偵察機類
- class Wraith implements flyAbility, concealAbility
- {
- //這個函數(shù)表示偵察機飛行的運行代碼 function fly()
- {
- //飛行的運行代碼
- }
- //這個函數(shù)表示偵察機隱形的運行代碼 function conceal()
- {
- //隱形的運行代碼
- }
- }
八、總結(jié)
我們討論了PHP面向?qū)ο蟮幕局R,通過星際爭霸這一經(jīng)典的游戲來說明,大家可以看到面向?qū)ο蟮某醪阶饔?。我們看到通過面向?qū)ο罂梢允勾a更加清晰,類將代碼組織起來,比較方便的重復(fù)使用。同時對象也減少了變量的沖突,方便相關(guān)性數(shù)據(jù)的保存和使用。如果要解決的問題涉及很多方面,面向?qū)ο罂梢匝莼龈屿`活和有技巧的方式,比如通常提到的設(shè)計模式,和很多框架。當然,面向?qū)ο笠灿腥秉c,從上面的代碼可以看到,首先代碼就多了,簡單的任務(wù)如果定義許多類,反而麻煩。對于簡單任務(wù),面向?qū)ο笠部赡苁勾a運行的效率降低。
原文鏈接:http://www.biaodianfu.com/starcraft-object-oriented.html