基於介面的編程
作為一個PHP程式員,學習如何基於介面進行編程改變了我的人生,大大提升了我通過整合第三方PHP組件來完善我的項目的能力。介面並不是新的功能,但是它卻是你日常工作中必須瞭解和使用的重要特性。
那麼PHP的介面到底是什嗎?介面是兩個PHP對象之間的契約,一個對象調用另一個對象時並不需要知道對方是什麼,而只需要知道對方能做什麼。介面可以降低我們代碼依賴關係的耦合性,允許我們的代碼調用任何實現了期望介面的第三方代碼。我們只需要關心第三方的代碼是否實現了介面就行,而根本不用關心第三方代碼是如何?這些介面的。我們來看一個現實中的例子。
假設我到佛羅里達的邁阿密參加Sunshine PHP開發人員大會。我想去城裡逛逛,所以我直接去了當地的汽車租賃公司。他們有一輛現代緊湊型的小車,一輛斯巴魯的旅行車和一輛布加迪威龍(太讓我驚訝了)。我知道我只是想藉助某個交通工具去城裡逛逛,這三輛車都可以滿足我的需求。但是每輛車又是那麼的與眾不同。現代雅紳特還不錯啦,但是我喜歡更有活力一點的。我沒有孩子,所以旅行車還是大了點。那麼,好吧就選布加迪好了。
現實是,我駕駛這三輛車中的任意一輛都可以,因為它們都共用了公用的、已知的介面。每輛車都有一個方向盤、一個油門踏板、一個刹車踏板和轉向燈,每輛車都使用汽油作為燃料。但是布加迪的動力強大到我都無法駕馭,但是現代車的駕駛介面卻和它一模一樣。因為三輛車都共用了同樣的已知的介面,但我有機會可以選擇我更喜歡的車型(實話實話,最終我可能還是會選擇現代)。
在PHP的物件導向方面也有同樣的概念。如果My Code中使用了某個特定的類(代表了特定的實現)的對象,那麼My Code的功能也固然變得非常有限,因為它只能永遠使用那個類的對象。然而,如果My Code將要用到的是一個介面,My Code立刻就能知道如何去使用任何實現了這個介面的對象。My Code根本不用關心介面是如何?的,My Code只關心對象是否實現了介面,讓我們用一個例子把這一切解釋清楚。
假設我們有一個PHP類名叫DocumentStore可以從不同的資料來源中擷取資料,它可以從一個遠端地址中擷取HTML,也可以從文檔流中讀取資料,還可以擷取終端的命令列輸出。每個儲存在DocumentStore執行個體中的文檔都有一個唯一的ID。例子 2-6 展示了DocumentStore類。
例子 2-6 DocumentStore類的定義
class DocumentStore{ protected $data = []; public function addDocument(Documentable $document) { $key = $document->getId(); $value = $document->getContent(); $this->data[$key] = $value; } public function getDocuments() { return $this->data; }}
如果addDocument方法只接收Documentable類的執行個體作為參數怎麼才能實現前面描述的功能?很好的觀察力。實際上Documentable不是一個類,而是一個介面,看看例子 2-7裡介面的定義
例子 2-7 Documentable介面的定義
interface Documentable{ public function getId(); public function getContent();}
在介面的定義中我們能看到任何實現了Documentable介面的對象都必須定義一個public的getId()方法和一個public的getContent()方法。
那麼這樣做的好處是什嗎?好處是我們可以分別構造多個功能迥異的文檔擷取類。例子 2-8展示了一個通過curl從遠程地址擷取HTML的介面實現。
例子 2-8 HtmlDocument類的定義
url = $url; } public function getId() { return $this->url; } public function getContent() { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_MAXREDIRS, 3); $html = curl_exec($ch); curl_close($ch); return $html; }}
另一個實現(例子 2-9)可以讀取給定資源中的資料流。
例子 2-9 StreamDocument類的定義
resource = $resource; $this->buffer = $buffer; } public function getId() { return 'resource-' . (int)$this->resource; } public function getContent() { $streamContent = ''; rewind($this->resource); while (feof($this->resource) === false) { $streamContent .= fread($this->resource, $this->buffer); } return $streamContent; }}
最後一個實現(例子 2-10)可以執行一個終端的命令並擷取執行結果
例子 2-10 CommandOutputDocument類的定義
command = $command; } public function getId() { return $this->command; } public function getContent() { return shell_exec($this->command); }}
例子 2-11 展示了我們如何使用DocumentStore類來操作我們實現了Documentable介面的三個文檔收集類
例子 2-11 DocumentStore
addDocument($htmlDoc);// Add stream document$streamDoc = new StreamDocument(fopen('stream.txt', 'rb'));$documentStore->addDocument($streamDoc);// Add terminal command document$cmdDoc = new CommandOutputDocument('cat /etc/hosts');$documentStore->addDocument($cmdDoc);print_r($documentStore->getDocuments());
這樣做最大的亮點在於HtmlDocument、StreamDocument和CommandOutputDocument類除了介面一致外其它方面完全不同。
時至今日,對介面進行編程創造出了更靈活的代碼,我們不再關心具體的實現,而是將這些工作交給其他人來完成。越來越多的人(譬如你的同事,你的開源項目的使用者,或者你從來都沒有見過面的開發人員)可以寫出與你能夠彼此無縫對接的代碼,而他們僅僅只需要瞭解介面即可。
以上就介紹了[Modern PHP] 第二章 新特性之二 基於介面的編程,包括了方面的內容,希望對PHP教程有興趣的朋友有所協助。