對象 本文描述 PHP V5 中對象和類的基礎知識,從最基本的概念一直講到繼承,主要針對經驗豐富的物件導向程式員和尚未接觸過對象的讀者。
作為 PHP 程式員,您肯定知道變數和函數。但類和對象可能就是另一回事。不定義單個類,就可以建立完美的系統。但即使您決定在自己的代碼中不使用物件導向的編程,您仍可能需要瞭解物件導向的編程。例如,如果使用第三方庫,比如通過 PHP Extension and Application Repository (PEAR) 可以使用的庫,您將發現自己在執行個體化對象和調用方法。
什麼是類和對象?
簡單地說,類 是一個由變數和方法組成的獨立塊或束。這些組件通常結合實現單個責任或一組責任。在本文中,您將建立一個類,該類收集了用於查詢和填充由項和值組成的詞典的方法。
類可以直接用作組織資料和功能的簡單方法,就像一組函數和變數一樣。但使用類可以忽略它的存在。類可用於在記憶體中產生多個執行個體。這樣的執行個體叫做對象。每個對象可以訪問一組相同的函數(在物件導向上下文中叫做方法)和變數(叫做特性或執行個體變數),但每個變數的實際值在每個對象中是不同的。
考慮角色扮演遊戲中的一個單元——比如坦克。類可能為坦克設定一組變數:防禦和進攻能力,範圍,健康情況,等等。該類也可能定義一組函數,其中包括 move() 和 attack()。當系統包含一個坦克類時,該類可用於產生數十個或數百個坦克對象,每個對象都潛在地具有自己的健康情況或範圍特徵。因此,類是用於產生對象的藍圖或模板。
理解類和對象最簡單的方法可能就是建立一些類和對象。
第一個類
可以用 class 關鍵字建立類。最簡單的情況是,類由關鍵字類、名稱和代碼塊組成:
class Dictionary {
}
類名可以包含字母、數字和底線的任何組合,但不能以數字打頭。
上例中的 Dictionary 類儘管用處有限,但完全合法。那麼如何使用該類來建立一些對象呢?
$obj1 = new Dictionary();
$obj2 = new Dictionary();
$obj3 = new Dictionary();
至少在形式上,執行個體化對象與調用函數相似。對於函數調用,必須提供圓括弧。與函數一樣,一些類需要您為其傳遞參數。您還必須使用 new 關鍵字。這就告訴 PHP 引擎您希望執行個體化一個新對象。然後,返回的對象可以儲存在一個變數中以供將來使用。
屬性
在類的主體中,可以聲明叫做屬性的特殊變數。在 PHP V4 中,屬性必須用關鍵字 var 調用。這仍是合法的文法,但主要是為了向後相容。在 PHP V5 中,屬性必須聲明為 public、private 或 protected。可以在 關鍵字:在此我們是否可以有一點隱私?中閱讀有關這些限定詞的內容。但現在在例子中將所有屬性聲明為 public。清單 1 顯示一個聲明了兩個屬性的類。
清單 1. 聲明兩個屬性的類
class Dictionary {
public $translations = array();
public $type ="En";
}
正如所看到的,可以同時聲明屬性並為其賦值。可以用 print_r() 函數快速探索一下對象的狀態。清單 2 顯示 Dictionary 對象現在具有更多成員。
清單 2. Dictionary 對象一覽
$en = new Dictionary();
print_r( $en );
如果運行該指令碼,將看到如下對象的輸出:
Dictionary Object
(
[translations] => Array
(
)
[type] => En
)
可以使用對象操作符 -> 訪問公用對象屬性。所以 $en->type 表示由 $en 引用的 Dictionary 對象的 $type 屬性。如果可以訪問屬性,就意味著可以設定和獲得其值。清單 3 中的代碼建立 Dictionary 類的兩個執行個體 —— 換言之,它執行個體化兩個 Dictionary 對象。它更改一個對象的 $type 屬性,並添加兩個對象的翻譯:
清單 3. 建立 Dictionary 類的兩個執行個體
$en = new Dictionary();
$en->translations['TREE'] = "tree";
$fr = new Dictionary();
$fr->type = "Fr";
$fr->translations['TREE'] = "arbre";
foreach ( array( $en, $fr ) as $dict ) {
print "type: {$dict->type} ";
print "TREE: {$dict->translations['TREE']}\n";
}
該指令碼輸出如下
type: En TREE: tree
type: Fr TREE: arbre
所以 Dictionary 類現在比較有用了。單個對象可以儲存不同的索引值組合,還有一個標誌,該標誌告訴用戶端有關這種 Dictionary 的詳細資料。
儘管 Dictionary 類當前與關聯陣列的封裝器相差無幾,但這裡有一些瞭解對象功能的線索。目前,我們已經可以很好地表示我們的樣本資料了,如清單 4 所示。
清單 4. 樣本資料
$en = array(
'translations'=>array( 'TREE' => 'tree' ),
'type'=>'En'
);
$fr = array(
'translations'=>array( 'TREE' => 'arbre' ),
'type'=>'Fr'
);
雖然該資料結構完成了與 Dictionary 類相同的目的,但它沒有提供結構的保證。如果傳遞 Dictionary 對象,我們知道它具有 $translations 屬性。但如果是一個關聯資料,則沒有這樣的保證。這個事實使得類似 $fr['translations']['TREE']; 的查詢有些碰運氣,除非進行查詢的代碼確定數組的起源。這是對象的重點:對象的類型是其特徵的保證。
雖然用Object Storage Service資料有優點,但是您可能沒有一點感覺。對象可以是東西,但關鍵在於它們還可以做事情。
方法
簡單地說,方法是在類中聲明的函數。它們通常(但不總是)通過對象執行個體使用對象操作符來調用的。清單 5 向 Dictionary 類中添加一個方法,並調用該方法。
清單 5. 向 Dictionary 類中添加方法
class Dictionary {
public $translations = array();
public $type ="En";
function summarize() {
$ret = "Dictionary type: {$this->type}\n";
$ret .= "Terms: ".count( $this->translations )."\n";
return $ret;
}
}
$en = new Dictionary();
$en->translations['TREE'] = "tree";
print $en->summarize();
它提供如下輸出:
Dictionary type: En
Terms: 1
正如所看到的,聲明 summarize() 方法與聲明任何函數的方式一樣,只不過它是在類中聲明。summarize() 方法是通過 Dictionary 執行個體使用對象操作符調用的。summarize() 函數訪問屬性來提供對象狀態的簡述。
注意對於本文來說的一個新特性的用法。$this 偽變數提供了一種用於對象引用自己的屬性和方法的機制。在對象外部,可以使用控制代碼來訪問它的元素(在本例子中是 $en)。在對象內部,則無此控制代碼,所以必須求助於 $this。如果覺得 $this 有些迷惑,則在代碼中遇到它時,試著在頭腦中用當前執行個體 替換它。
類通常使用通用建模語言 (Universal Modeling Language,UML) 表示在圖表中。UML 的詳細資料超出了本文的範圍,但這種圖表不過是一種可視化類別關係的好方法。圖 1 顯示用 UML 表示的 Dictionary 類。類名位於頂層,屬性在中間,方法在底層。
圖 1. 使用 UML 顯示 Dictionary 類
建構函式
PHP 引擎識別許多“魔術”方法。如果定義了方法,則 PHP 引擎將在相應的情況發生時自動調用這些方法。最常實現的方法是建構函式方法。PHP 引擎在執行個體化對象時調用建構函式。對象的所有基本設定代碼都放在建構函式中。在 PHP V4 中,通過聲明與類同名的方法來建立建構函式。在 V5 中,應聲明叫做 __construct() 的方法。清單 6 顯示需要 DictionaryIO 對象的建構函式。
清單 6. 需要 DictionaryIO 對象的建構函式
class Dictionary {
public $translations = array();
public $type;
public $dictio;
function __construct( $type, DictionaryIO $dictio ) {
$this->type = $type;
$this->dictio=$dictio;
}
//...
要執行個體化 Dictionary 對象,需要將類型字串和 DictionaryIO 對象傳遞給它的建構函式。建構函式使用這些參數來設定自有屬性。下列代碼顯示可以如何執行個體化 Dictionary 對象: $en = new Dictionary( "En", new DictionaryIO() );
Dictionary 類現在比以前更安全。所有 Dictionary 對象都已經用必要參數初始化過了。
當然,還無法阻止一些人隨後更改 $type 屬性或將 $dictio 設定為空白。可喜的是,PHP V5 可以協助您實現這一功能。
關鍵字:在此我們是否可以有一點隱私?
前面已經看到與屬性聲明相關的 public 關鍵字。該關鍵字表示屬性的可見度。事實上,屬性的可見度可以設定為 public、private 和 protected。聲明為 public 的屬性可以在類外部寫入和讀取,聲明為 private 的屬性只在對象或類上下文中可見。聲明為 protected 的屬性只能在當前類及其子類的上下文中可見。(在 繼承 部分將會看到這些內容起作用。)可以使用 private 屬性來真正鎖定類。如果將屬性聲明為 private 並試圖從類範圍外部存取它(如清單 7 所示),PHP 引擎將拋出致命錯誤。
清單 7. 試圖從類範圍外部存取屬性
class Dictionary {
private $translations = array();
private $dictio;
private $type;
function __construct( $type, DictionaryIO $dictio ) {
$this->type = $type;
$this->dictio = $dictio;
}
// ...
}
$en = new Dictionary( "En", new DictionaryIO() );
$en->dictio = null;
輸出如下: Fatal error: Cannot access private property
Dictionary::$dictio in...
一般來說,應將大多數屬性聲明為 private,然後根據需要提供獲得和設定這些屬性的方法。這樣就可以控制類的介面,使一些資料唯讀,在將參數分配給屬性之前對參數進行清理或過濾,並提供與對象互動的一套明確的規則。
修改方法可見度的方法與修改屬性可見度的方法一樣,即在方法聲明中添加 public、private 或 protected。如果類需要使用一些外部世界無需知道的家務管理方法,則可以將其聲明為 private。在清單 8 中,get() 方法為 Dictionary 類的使用者提供了提取翻譯的介面。該類還需要跟蹤所有查詢,因此提供了 private 方法 logQuery()。
清單 8. get() 方法為 Dictionary 類的使用者提供了介面
function get( $term ) {
$value = $this->translations[$term];
$this->logQuery( $term, $value, "get" );
return $value;
}
private function logQuery( $term, $value, $kind ) {
// write log information
}
將 logQuery() 聲明為 private 簡化了公用介面,而且防止了類不適當地調用 logQuery()。與屬性一樣,嘗試從包含類外部調用私人方法將導致致命錯誤。
[1] [2] 下一頁