什麼是多態性?
Polymorphism(多態性)是一個很長的單詞,但是它表示的是一個非常簡單的概念。
多態性描述了在面向對象編程模式中類有不同的功能,而共享一個通用的接口。
多態性的優點是,並不需要知道它使用的是哪一個類,因為他們都用同樣的方式與不同的類的代碼工作。
可將多態性類比成現實世界的一個按鈕。大家都知道如何使用一個按鈕:您只需給它施加壓力。一個按鈕“確實是這樣”,然而,取決於它和什麼連接和使用它的上下文 - 但結果並不影響它是如何使用。如果你的老板告訴你按下一個按鈕,您已經有執行任務所需的所有信息。
在編程的世界中,多態性是用來使應用程序更加模塊化和可擴展的。相比於凌亂的條件語句描述的行動不同的課程,您可以創建根據您的需求選擇的互換對象。這是多態性的基本目標。
Interfaces接口
接口是與類(class)類似的,除了它不能包含代碼。接口可以定義方法名稱和參數,但不是方法的內容。實現接口的任何類都必須實現接口中定義的所有方法。一個類可以實現多個接口。
使用的“interface”關鍵字聲明一個接口:
復制代碼 代碼如下:
interface MyInterface {
// methods
}
被附加到一個類,使用“implements”關鍵字(多個接口,可以通過用逗號分開):
復制代碼 代碼如下:
class MyClass implements MyInterface {
// methods
}
在接口中可以像在類中一樣定義方法,除了沒有方法體(在大括號中的部分)以外。
復制代碼 代碼如下:
interface MyInterface {
public function doThis();
public function doThat();
public function setName($name);
}
在這裡定義的所有方法都必須如接口中所描述地那樣被包含在任何實現它的類中。(讀下面的代碼注釋)
復制代碼 代碼如下:
//合法的 VALID
class MyClass implements MyInterface {
protected $name;
public function doThis() {
// code that does this
}
public function doThat() {
// code that does that
}
public function setName($name) {
$this->name = $name;
}
}
// 非法的INVALID
class MyClass implements MyInterface {
// missing doThis()!
private function doThat() {
// this should be public!
}
public function setName() {
// missing the name argument!
}
}
抽象類Abstract Class
抽象類是接口和類的混合。它可以像接口一樣定義方法。繼承自抽象類的類必須實現抽象類中定義的所有抽象方法。
抽象類的定義方式與類一樣,不過是在前面附加了一個abstract 關鍵字。
復制代碼 代碼如下:
abstract class MyAbstract {
// methods
}
並且 是用 ‘extends‘ 關鍵字附加到類:
復制代碼 代碼如下:
class MyClass extends MyAbstract {
// class methods
}
就像在普通類中一樣,普通的方法以及任何抽象方法(使用關鍵字“abstract”)可以在抽象類中定義。抽象方法的行為就像在接口中定義的的方法,而且繼承它的擴展類中必須實現完全一樣的定義。
復制代碼 代碼如下:
abstract class MyAbstract {
public $name;
public function doThis() {
// do this
}
abstract public function doThat();
abstract public function setName($name);
}
我們假設你有一個文章Article類負責管理你網站上的文章。它包含關於文章的信息,包括:title, author, date, and category.
就像下面這樣:
復制代碼 代碼如下:
class poly_base_Article {
public $title;
public $author;
public $date;
public $category;
public function __construct($title, $author, $date, $category = 0) {
$this->title = $title;
$this->author = $author;
$this->date = $date;
$this->category = $category;
}
}
注意:在這個教程中的示例類使用了“package_component_Class”的命名約定,這是一個用來將類名分隔到虛擬的命名空間來避免命名沖突的通用方法。
現在你想添加一個方法來輸出各種不同格式的信息,如XML和JSON。你也許會打算像下面這樣做:
復制代碼 代碼如下:
class poly_base_Article {
//...
public function write($type) {
$ret = '';
switch($type) {
case 'XML':
$ret = '';
$ret .= '';
$ret .= '' . $obj->author . '';
$ret .= '' . $obj->date . '';
$ret .= '' . $obj->category . '';
$ret .= '';
break;
case 'JSON':
$array = array('article' => $obj);
$ret = json_encode($array);
break;
}
return $ret;
}
}
這種解決方案是丑陋的,但是它是可靠的——至少在現在。問下你自己將來會發生什麼,當我們需要添加更多格式的時候?你可以繼續編輯這個類,然後添加越來越多的case ,
但是現在你只是在稀釋(FIX ME: diluting) 你的類。
OOP的一個重要原則是一個類應該做的一件事情,而應該把它做好。
考慮到這一點,條件語句應該是一個紅色的標志,表明你的類是試圖做太多不同的東西。這是多態性的用武之地。
在我們的例子中,有兩個任務明確的提出:管理文章和格式化其數據。在本教程中,我們將重構我們的格式化代碼到一個新的類,然後我們會發現使用多態性是多麼容易。
Step 2: 定義你的接口 Define Your Interface
第一件事就是我們應該定義接口,努力想好怎麼定義你的接口是件重要的事情,因為對它的任何改變都將需要改動調用它的代碼。
在我們這個例子中,我們將使用一個簡單的接口來定義一個方法:
復制代碼 代碼如下:
interface poly_writer_Writer {
public function write(poly_base_Article $obj);
}
它就是那樣簡單,我們已經定義了一個公用方法write() ,它接受一個文章對象作為參數。任何實現Writer接口的類都將會確保有這個方法。
小提示:如果你想嚴格限制傳遞給你的方法和函數的參數類型,你可以使用類型提示,就像我們已經在write()方法中做的一樣,它只能接受poly_base_Article 對象類型的數
據。不幸的是,在目前版本的PHP中,返回類型提示是不被支持的,所以,你要小心返回值的類型了。
Step 3: 創建實現類 Create Your Implementation
定義接口後,該是時候來創建類來真正干活的啦。在我們的例子中,我們需要輸出兩種格式。這樣,我們就要兩個Writer 類:XMLWriter 和 JSONWriter 。從傳遞過來的
Article 對象提取數據然後格式化這些信息完全取決於這些類了。
下面是 XMLWriter 類的一個示例:
復制代碼 代碼如下:
class poly_writer_XMLWriter implements poly_writer_Writer {
public function write(poly_base_Article $obj) {
$ret = '';
$ret .= '';
$ret .= '' . $obj->author . '';
$ret .= '' . $obj->date . '';
$ret .= '' . $obj->category . '';
$ret .= '';
return $ret;
}
}
就如你可以從類定義中看到的一樣,我們使用implements關鍵字來實現我們的接口。write() 方法包含格式化為XML的功能。
現在我們來看下JSONWriter 類:
復制代碼 代碼如下:
class poly_writer_JSONWriter implements poly_writer_Writer {
public function write(poly_base_Article $obj) {
$array = array('article' => $obj);
return json_encode($array);
}
}
現在,我們的代碼中的特定每種格式都包含在單獨的類。每個類有全權負責處理特定的格式,而不是其他。您的應用程序中沒有其他部分需要關心這些是如何工作的才能使用它,
感謝我們的接口。
Step 4: 使用你的接口Use Your Implementation
在我們的新類定義後,該是時候來重溫一下我們的Article類了,所有原write() 方法中的代碼已經被分離出來,進入到我們的新類中了。
我們的所有方法現在需要做的就是使用這些新的類,像這樣:
復制代碼 代碼如下:
class poly_base_Article {
//...
public function write(poly_writer_Writer $writer) {
return $writer->write($this);
}
}
獲取一個 Writer對象 Obtaining A Writer
你也許會疑惑你該從哪裡獲取一個 Writer對象開始,因為你需要傳遞一個 Writer對象到這個方法。
這完全取決於你,並且,有很多策略。如,你可能會使用工廠類來獲取請求數據然後創建一個對象:
復制代碼 代碼如下:
class poly_base_Factory {
public static function getWriter() {
// grab request variable
$format = $_REQUEST['format'];
// construct our class name and check its existence
$class = 'poly_writer_' . $format . 'Writer';
if(class_exists($class)) {
// return a new Writer object
return new $class();
}
// otherwise we fail
throw new Exception('Unsupported format');
}
}
就像我說的,根據你的需求,有好多其它策略可用。在這個例子中,通過一個請求變量選擇哪種格式是要使用的。它基於request請求變量來構造一個類名,檢測它是否存在,
然後返回一個新的Writer對象。如果沒有那個名字的類存在,拋出一個異常,讓客戶端代碼決定接下來要干什麼。
Step 5: 把它們放一起 Put It All Together
當所有東東都到位了,下面是我們的客戶端代碼如何放在一起:
復制代碼 代碼如下:
$article = new poly_base_Article('Polymorphism', 'Steve', time(), 0);
try {
$writer = poly_base_Factory::getWriter();
}
catch (Exception $e) {
$writer = new poly_writer_XMLWriter();
}
echo $article->write($writer);
首先,我們創建了一個示例 Article 對象來配合工作。然後,我們試圖從工廠Factory獲取一個Factory對象,如果異常發生的話回滾到默認(XMLWriter) 。
最後,我們傳遞Writer對象給我們的Article的 write() 方法,輸出結果。
結論 Conclusion
在本教程中,我提供了一個多態性的簡介而且解釋了PHP中的接口。我希望你意識到,我只向您展示一個潛在的使用多態性的案例。
多態性是以一個優雅的方式來避免您的OOP代碼中丑陋的條件語句。它遵循的原則是使您的組件分離,而且它是許多設計模式的組成部分。如果您有任何問題,不要猶豫,在評論中提問!
譯自:http://net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/
原文發表在:http://ihacklog.com/?p=4703