首頁 >後端開發 >php教程 >[Modern PHP] 第二章 新特性之二 基於介面的編程

[Modern PHP] 第二章 新特性之二 基於介面的編程

WBOY
WBOY原創
2016-07-30 13:31:19976瀏覽

基於介面的程式設計

作為一個PHP程式設計師,學習如何基於介面進行程式設計改變了我的人生,大大提升了我透過整合第三方PHP元件來完善我的專案的能力。介面並不是新的功能,但是它卻是你日常工作中必須了解和使用的重要特性。

那麼PHP的介面到底是什麼?介面是兩個PHP物件之間的契約,一個物件呼叫另一個物件時並不需要知道對方是什麼,而只需要知道對方能做什麼。介面可以降低我們程式碼依賴關係的耦合性,允許我們的程式碼呼叫任何實現了期望介面的第三方程式碼。我們只需要關心第三方的程式碼是否實作了介面就行,而根本不用關心第三方程式碼是如何實作這些介面的。我們來看一個現實中的例子。

假設我到佛羅裡達的邁阿密參加Sunshine PHP開發者大會。我想去城裡逛逛,所以我直接去了當地的汽車租賃公司。他們有一輛現代緊湊型的小車,一輛斯巴魯的旅行車和一輛布加迪威龍(太讓我驚訝了)。我知道我只是想藉助某個交通工具去城裡逛逛,這三輛車都可以滿足我的需求。但是每輛車又是那麼的與眾不同。現代雅紳特還不錯啦,但是我喜歡更有活力一點的。我沒有孩子,所以旅行車還是大了點。那麼,好吧就選布加迪好了。

現實是,我駕駛這三輛車中的任意一輛都可以,因為它們都共享了公用的、已知的介面。每輛車都有一個方向盤、一個油門踏板、一個煞車踏板和方向燈,每輛車都使用汽油作為燃料。但是布加迪的動力強大到我都無法駕馭,但是現代車的駕駛接口卻和它一模一樣。因為三輛車都共享了同樣的已知的接口,但我有機會可以選擇我更喜歡的車型(實話實話,最終我可能還是會選擇現代)。

在PHP的物件導向方面也有同樣的概念。如果我的程式碼中使用了某個特定的類別(代表了特定的實作)的對象,那麼我的程式碼的功能也固然變得非常有限,因為它只能永遠使用那個類別的對象。然而,如果我的程式碼將要用到的是一個接口,我的程式碼立刻就能知道如何去使用任何實現了這個接口的對象。我的程式碼根本不用關心接口是如何實現的,我的程式碼只關心對像是否實現了接口,讓我們用一個例子把這一切解釋清楚。

假設我們有一個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類別的定義

<?php
class HtmlDocument implements Documentable
{
    protected $url;

    public function __construct($url)
    {
        $this->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類別的定義

<?php
class StreamDocument implements Documentable
{
    protected $resource;
    protected $buffer;

    public function __construct($resource, $buffer = 4096)
    {
        $this->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 CommandDDocument類的定義範例2-11 展示了我們如何使用DocumentStore類別來操作我們實作了Documentable介面的三個文件收集類別


範例2-11 DocumentStore

<?php
class CommandOutputDocument implements Documentable
{
    protected $command;

    public function __construct($command)
    {
        $this->command = $command;
    }

    public function getId()
    {
        return $this->command;
    }

    public function getContent()
    {
        return shell_exec($this->command);
    }
}
DocuDocucumentStore除了接口一致外其它方面完全不同。
時至今日,對介面進行程式設計創造出了更靈活的程式碼,我們不再關心具體的實現,而是將這些工作交給其他人來完成。越來越多的人(譬如你的同事,你的開源專案的用戶,或者你從來沒有見過面的開發者)可以寫出與你能夠彼此無縫對接的程式碼,而他們僅僅只需要了解接口即可。

以上就介紹了[Modern PHP] 第二章 新特性之二 基於介面的編程,包括了方面的內容,希望對PHP教程有興趣的朋友有所幫助。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn