中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在PHP中使用接口編寫優雅的代碼

發布時間:2022-07-26 09:34:29 來源:億速云 閱讀:122 作者:iii 欄目:編程語言

今天小編給大家分享一下怎么在PHP中使用接口編寫優雅的代碼的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

怎么在PHP中使用接口編寫優雅的代碼

在編程中,確保代碼可讀、可維護、可擴展和易于測試是很重要的;而使用接口,恰恰是我們改進代碼中所有這些因素的方法之一。

目標讀者

本文的目標讀者是對 OOP(面向對象編程)概念有基本了解并在 PHP 中使用繼承的開發人員。如果你知道如何在 PHP 代碼中使用繼承,那么你應該可以很好地理解本文。

什么是接口?

簡而言之,接口只是對類應該做什么的描述,它們可用于確保實現該接口的任何類都將包括在其內部定義的每個公共方法。

接口可以

  • 用于定義類的公共方法;

  • 用于定義類的常量。

接口不可以

  • 被實例化;

  • 用于定義類的私有或受保護方法;

  • 用于定義類的屬性。

接口是用來定義一個類應該包括的公共方法的。記住,你只需要在接口里定義方法的簽名,而不需要包含方法的主體(就像通常在類中看到的方法一樣)。**這是因為接口僅用于定義對象之間的通信,而不是像在類中那樣定義通信和行為。**為了說明這個問題,下面展示了一個定義了幾個公共方法的示例接口:

interface DownloadableReport
{
    public function getName(): string;

    public function getHeaders(): array;

    public function getData(): array;
}

根據 php.net 文檔我們可以知道,接口有兩個主要用途:

  • 允許開發者創建不同類別的對象,這些對象可以互換使用,因為它們實現了相同的一個或多個接口。常見的例子包含:多個數據庫訪問服務、多個支付網關、不同的緩存策略等。不同的實現之間可以互換,而不需要對使用它們的代碼進行任何修改。

  • 允許函數或方法接受符合接口的參數并對其進行操作,而不關心該對象還可以做什么或它是如何實現的。這些接口通常被命名為 IterableCacheableRenderable 等,來說明這些接口的實際含義。

在 PHP 中使用接口

接口是 OOP(面向對象編程)代碼庫的重要部分。接口能讓我們降低代碼耦合并提高可擴展性。舉個例子,讓我們看看下面這個類:

class BlogReport
{
    public function getName(): string
    {
        return 'Blog report';
    }
}

如你所見,我們定義了一個類,類中有一個函數,返回一個字符串。這樣一來,我們定義了該方法的行為,所以我們知道 getName() 是如何返回字符串的。不過,假設我們在另一個類調用這個方法;這個類不需要關心這個字符串如何構建的,它只關心該方法是否返回內容。舉例來說,讓我們看看如何在另一個類調用此方法:

class ReportDownloadService
{
    public function downloadPDF(BlogReport $report)
    {
        $name = $report->getName();

        // 下載文件……
    }
}

盡管上面的代碼正常運行,但我們設想一下,現在想給 UsersReport 類中增加下載用戶報告的功能。顯然,我們不能使用 ReportDownloadService 中的現有方法,因為我們已經強制規定方法只能傳遞 BlogReport 類。因此,我們必須修改把原有的下載方法名稱改掉(避免重名),然后另外再添加一個類似的方法,如下所示:

class ReportDownloadService
{
    public function downloadBlogReportPDF(BlogReport $report)
    {
        $name = $report->getName();

        // 下載文件……
    }

    public function downloadUsersReportPDF(UsersReport $report)
    {
        $name = $report->getName();

        // 下載文件……
    }
}

假設上面的方法中的下載文件部分(注釋掉的部分)使用了相同的代碼,而且我們可以將這些相同的代碼單獨寫成一個方法,但我們仍會有一些重復的代碼(譯者注:指的是每個方法中都會有 $name = $report->getName();)以及有多個幾乎相同的類的入口。這可能會給將來擴展代碼或測試帶來額外的工作量。

例如,假設我們創建了一個新的 AnalyticsReport;我們現在需要向該類添加一個新的 downloadAnalyticsReportPDF() 方法。你可以清晰的看到這個文件將如何膨脹(譯者注:指每增加一個類型,就要增加一個下載方法)。這就是一個使用接口的完美場景!

讓我們從創建第一個接口開始:讓我們將其命名為 DownloadableReport,定義如下:

interface DownloadableReport
{
    public function getName(): string;

    public function getHeaders(): array;

    public function getData(): array;
}

我們現在可以更新 BlogReportUsersReport 來實現 DownloadableReport 接口,如下例所示。但是請注意,作為演示用途,我故意把 UsersReport 中的代碼寫錯了:

class BlogReport implements DownloadableReport
{
    public function getName(): string
    {
        return 'Blog report';
    }

    public function getHeaders(): array
    {
        return ['The headers go here'];
    }

    public function getData(): array
    {
        return ['The data for the report is here.'];
    }
}
class UsersReport implements DownloadableReport
{
    public function getName()
    {
        return ['Users Report'];
    }

    public function getData(): string
    {
        return 'The data for the report is here.';
    }
}

但當我們嘗試運行代碼的時候,我們將會收到錯誤,原因如下:

  • 缺少 getHeaders() 方法.

  • getName() 方法不包括接口的方法簽名中定義的返回類型。

  • getData() 方法定義了一個返回類型,但它與接口的方法簽名中定義的類型不同。

因此,為了修復 UsersReport 使其正確實現 DownloadableReport 接口,我們可以將其修改為:

class UsersReport implements DownloadableReport
{
    public function getName(): string
    {
        return 'Users Report';
    }

    public function getHeaders(): array
    {
       return [];
    }

    public function getData(): array
    {
        return ['The data for the report is here.'];
    }
}

現在兩個報告類都實現了相同的接口,我們可以這樣更新我們的 ReportDownloadService

class ReportDownloadService
{
    public function downloadReportPDF(DownloadableReport $report)
    {
        $name = $report->getName();

        // 下載文件……
    }

}

我們現在可以把 UsersReportBlogReport 對象傳入 downloadReportPDF 方法中,而且不會出現任何錯誤。這是因為我們知道該對象實現了報告類的必要方法,并且將返回我們期望的數據類型。

通過向方法傳遞了一個接口,而不是一個具體的類,我們可以根據方法的實際作用(而不是方法的實現原理)來解耦 ReportDownloadService類和這些報告類。

如果我們想創建一個新的 AnalyticsReport,我們可以讓它實現相同的接口。這樣一來,我們不必添加任何新的方法,只需要將報告對象傳遞給同一個的 downloadReportPDF() 方法。如果你正在構建你自己的包或框架,接口可能對你特別有用。你只需要告訴使用者要實現哪個接口,然后他們就可以創建自己的類。例如,在 Laravel 中,我們可以通過實現 Illuminate\Contracts\Cache\Store 接口來創建自己的自定義緩存驅動類。

除了能改進代碼之外,我喜歡使用接口的另一個原因是 —— 它們起到了“代碼即文檔”的作用。例如,如果我想弄清楚一個類能做什么,不能做什么,我傾向于先看接口,然后再看實現它的類。接口能夠告訴我們所有可被調用的方法,而不需要我們過多地關心這些方法的底層實現方式是怎樣的。

值得注意的是,Laravel 中的“契約(contract)”和“接口(interface)”這兩個詞語是可互換的。根據 Laravel 文檔,“契約是一組由框架提供的核心服務的接口”。所以,記住:契約是一個接口,但接口不一定是契約。通常情況下,契約只是框架提供的一個接口。關于使用契約的更多信息,我建議大家可以閱讀這一篇文檔。它很好地剖析了契約究竟是什么,也對使用契約的方式與場景做了一定的敘述。

以上就是“怎么在PHP中使用接口編寫優雅的代碼”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

php
AI

台前县| 曲阜市| 会泽县| 马关县| 桦南县| 微山县| 威远县| 大英县| 钟祥市| 定陶县| 汤原县| 长岭县| 邢台县| 颍上县| 昌乐县| 惠水县| 务川| 卓资县| 长丰县| 永安市| 长春市| 拜泉县| 中卫市| 玉屏| 凤庆县| 宜黄县| 霸州市| 鞍山市| 敦煌市| 即墨市| 紫阳县| 赫章县| 上杭县| 开远市| 威宁| 长垣县| 道真| 柳林县| 平阴县| 永新县| 德阳市|