用星際爭霸講解面向對象的概念

        在學習PHP的時候,感受本身對面向對象理解還不深入,不少時候是一頭霧水。經過別人的推薦,找到了這篇經過星際爭霸來說解面向對象概念的文章,轉載出來供有須要的朋友學習。

   1、類和對象 php

   若是玩家制造了一個機槍兵,那麼咱們怎麼表示他呢,由於每一個機槍兵有幾個基本的數據要記錄:剩餘的血,殺敵數量,攻擊力等等。咱們能夠用一個數組來記錄一個機槍兵剩餘的血和殺敵數量,由於這對於每一個機槍兵是獨立的。但攻擊力比較麻煩,由於通過升級,攻擊力會增長,這就必需要找出全部表示機槍兵的數組,而後進行修改,很是麻煩。從這裏咱們能夠看出一件事情,首先每一個機槍兵有獨立的數據須要記錄和修改,好比剩餘的血。同時他們有相同的數據須要共用,好比攻擊力。這時候面向對象就能幫上咱們的忙了。 設計模式

   1.一、類的定義 數組

   咱們先來處理一部分問題,也就是每一個機槍兵獨有的數據。 框架

class marine
{
   public $blood = 50; //剩餘的血
   public $kills = 0; //殺敵數量
   //這個函數(一般叫作方法)表示攻擊敵人時候的運行代碼    function attack($enemy)
   {
   //攻擊敵人的代碼
   }
}

   這叫作類,咱們創建了一個表示全部機槍兵的類marine,這裏面保留了須要每一個兵獨有的數據,好比上面代碼裏的剩餘的血。 函數

   1.二、對象的建立和使用 學習

   接下來咱們來使用對象,也就是每一個機槍兵: ui

$m1 = new marine();

   經過new後面加一個類的名字和括號,咱們新建了一個機槍兵$m1,$m1被叫作類marine的對象,咱們能夠把它想象成一個特殊變量,只不過裏面保存了多個數據。若是須要使用或者操做某個機槍兵的血(對象的屬性),只要用$m1->blood來表示就能夠了: this

echo $m1->blood;//輸出機槍兵$m1剩餘的血

   咱們再創建一個機槍兵 spa

$m2 = new marine();

   若是此時$m1被敵人攻擊過了,還剩下10個血。而$m2沒受過攻擊: 設計

echo $m1->blood;//結果是10
echo $m2->blood;//結果是50

   使用對象能夠很簡單的保存每一個機槍兵的血,不會互相影響。若是機槍兵$m1攻擊敵人的時候,能夠這樣使用對象的方法:

$m1->attack($z1);//假設攻擊的是某個小狗的對象$z1

   不一樣的類內能夠用同名的函數,好比小狗的類Zergling裏面也能夠有一個函數attack。要注意的是,從PHP5開始,不管在哪裏改變一個對象的屬性,都能改變它。好比上面一個小狗對象被做爲參數傳入機槍兵的attack函數,執行函數以後這個小狗對象的血減小了,這和通常的函數不一樣。但這是很直觀的,若是一個小狗被攻擊了,它的血就應該減小。

   2、構造函數和析構函數

   每次咱們新建一個機槍兵的時候,總人口應該加1,若是一個機槍兵被殺,人口應該減小1。能夠經過構造函數和析構函數來自動處理:

class marine
{
   //構造函數
   function __construct()
   {
   //增長總人口的代碼
   }
   //析構函數
   function __destruct()
   {
   //減小總人口的代碼
   }
}

   在一個類中,名字爲__construct的函數叫作構造函數,每次new新建一個類的對象的時候就會執行:

$m1 = new marine();//每次製造一個機槍兵時系統會調用類marine的構造函數,自動增長總人口

   在一個類中,名字爲__destruct的函數叫作析構函數,每次銷燬一個類的對象的時候就會執行:

unset($m1);//unset能夠用於對象,表示銷燬一個對象。每次一個機槍兵被殺時系統會調用類marine的析構函數,自動減小總人口

   3、靜態

   機槍兵的攻擊力是屬於全部機槍兵對象,每一個機槍兵的攻擊力都是同樣的,若是升級,應該一塊兒變化。這就用到static,表示靜態:

class marine
{
   static $attackNumber = 10; //攻擊力的數字
   //這個函數表示攻擊敵人時候的運行代碼    function attack($enemy)
   {
   //攻擊敵人的代碼,$enemy->blood表示敵人對象的血屬性
   $enemy->blood -= self::$attackNumber;
   }
}

   靜態屬性表示類全部的對象都共享的屬性,一旦改變,全部的對象都跟着變化。靜態屬性用static開頭,好比上面的static $attackNumber。靜態屬性能夠用類直接訪問:

echo marine::$attackNumber;//顯示10

   若是類之內的函數訪問,用self::$attackNumber表示本類的$attackNumber屬性。因此若是咱們升級了機槍兵的攻擊力,全部的機槍兵都受影響,這就是面向對象的好處之一,也解決了咱們前面討論的共同數據的問題。函數也能夠是靜態的,這樣就能夠用類直接訪問,不須要新建對象來調用:

class marine
{
   static $attackNumber = 10; //攻擊力的數字
   //這個函數表示機槍兵升級的運行代碼    static  function upgrade()
   {
   self::$attacknum++;
   }
}

   若是科技建築升級完畢,直接就調用這個函數:

marine::upgrade();

   4、繼承

   兵營用來造機槍兵,坦克房用來製造坦克,他們都是建築,可是卻有不少不一樣,若是用一個類「建築」來表示,很困難。但咱們要保留他們的共性,好比都能飛行,不但願飛行的代碼在各個類重複寫,又要讓他們能各自獨立的生產不一樣的東西。因此咱們能夠用繼承來處理,繼承表示父子關係,被繼承的叫父類,繼承的叫子類。用extends表示繼承

//建築類
class building
{
   function fly()
   {
   //建築飛行的代碼
   }
}
//兵營類
class marineBuilding extends building
{
   function createMarine()
   {
   //製造機槍兵的代碼
   }
}
//坦克房類
class tankBuilding extends building
{
   function createTank()
   {
   //製造坦克的代碼
   }
}

   接下來,咱們看看繼承產生的效果:

//若是造了一個兵營:
$mb1 = new marineBuilding();
/**
一旦他須要飛行,就能夠直接使用建築類的函數fly(),儘管兵營類的定義裏沒有這個函數
*/
$mb1->fly();
//而他要製造機槍兵的時候:
$mb1->createMarine();

   一樣是繼承建築類的坦克房類,就沒法制造機槍兵,由於這是兵營類的個性。若是在子類中的函數調用父類的函數,要使用parent,好比parent::fly()。注意,一個類只能有一個父類,PHP不容許多重繼承,也就是說一個孩子只能有一個爹,一個爹能夠有N個孩子!

   5、訪問控制

   若是用$attackNumber = 10表示屬性的話,系統默認是public $attackNumber = 10,因此建議這樣寫:

class marine
{
public static $attackNumber = 10; //攻擊力的數字
}

   public表示這個屬性是公共的,也就是在任何地方均可以訪問和操做的。但這就存在一些問題,若是有玩家知道了類marine的一些代碼結構,那他作個簡單的補丁程序,運行的時候加載上去:

//補丁
marine::$attackNumber = 10000;

   這樣的話,他的機槍兵有10000的攻擊力,呵呵,這樣的話,誰打得過他!爲此咱們要用private,表示這個屬性只有類裏面的函數才能訪問:

class marine
{
    private static $attackNumber = 10; //攻擊力的數字
   //這個函數表示機槍兵升級的運行代碼    function upgrade()
   {
      //這樣防止無限升級
      if(self::$attacknum<13)
      {
      self::$attacknum++;
      }
   }
}

   這樣一來,只有升級才能改變機槍兵的攻擊力。可是如今每每是團隊開發,並且不少用到類的繼承,若是private的話,子類就沒法訪問了,但又不但願隨便均可以修改某些屬性。那麼能夠用protected,protected的屬性能夠被子類的函數訪問。

   6、重載

   6.一、屬性重載

   若是咱們把地面部隊做爲一個類,讓機槍兵類來繼承他,這時候若是地面部隊類和機槍兵類裏面都定義了攻擊力$attackNumber,那麼每一個兵的攻擊力就決定於機槍兵類,而不是地面部隊。這就叫作重載。

//地面部隊
class groundArmy
{
public $attackNumber = 5;
}
//機槍兵
class marine extends groundArmy
{
public $attackNumber = 10; //攻擊力的數字
}
$m1 = new marine();//新建一個機槍兵
echo $m1->attackNumber;//顯示攻擊力爲10

   6.二、函數重載

   重載也能夠用於函數,子類的函數若是和父類函數同名,除非另行說明,不然子類的對象默認調用子類內的函數。好比人族的鬼兵類ghost和神族類的黑暗聖堂類(隱刀),都是隱形兵種,可是鬼兵隱形的時候會減小能量,黑暗聖堂根本沒有能量屬性。若是咱們把隱形能力做爲父類,鬼兵類ghost和神族類的黑暗聖堂類DarkTemplar來繼承它,同時實現不一樣的隱形代碼:

//隱形能力類
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();

   7、接口

   PHP不容許多重繼承,那麼有些問題就難辦了。假如爲了規範處理,咱們把隱形的能力創建一個類,而後把飛行能力放一個類,那麼人族的偵察機怎麼處理?不能繼承兩個類!那咱們不用繼承也行,可是開發組的其餘人一旦涉及到偵察機,要把長長的代碼讀一遍嗎?有沒有可能知道類的全部方法的簡要描述?能夠用到接口interface,一個類能夠執行(繼承)多個接口,接口中定義的函數不能有函數體,執行接口的類必須將這些函數完整定義。這樣咱們知道偵察機實現了飛行能力接口,必然有接口裏面描述的飛行方法:

//隱形能力的接口
interface concealAbility
{
public function conceal();
}
//飛行能力的接口
interface flyAbility
{
public function fly();
}
//偵察機類
class Wraith implements flyAbility, concealAbility
{
   //這個函數表示偵察機飛行的運行代碼    function fly()
   {
      //飛行的運行代碼
   }
   //這個函數表示偵察機隱形的運行代碼    function conceal()
   {
      //隱形的運行代碼
   }
}

   8、總結

   咱們討論了PHP面向對象的基本知識,經過星際爭霸這一經典的遊戲來講明,你們能夠看到面向對象的初步做用。咱們看到經過面向對象可使代碼更加清晰,類將代碼組織起來,比較方便的重複使用。同時對象也減小了變量的衝突,方便相關性數據的保存和使用。若是要解決的問題涉及不少方面,面向對象能夠演化出更加靈活和有技巧的方式,好比一般提到的設計模式,和不少框架。固然,面向對象也有缺點,從上面的代碼能夠看到,首先代碼就多了,簡單的任務若是定義許多類,反而麻煩。對於簡單任務,面向對象也可能使代碼運行的效率下降

相關文章
相關標籤/搜索