首頁  >  文章  >  php框架  >  Laravel 面向介面程式設計(實作)

Laravel 面向介面程式設計(實作)

Guanhui
Guanhui轉載
2020-05-21 11:06:213546瀏覽

Laravel 面向介面程式設計(實作)

面向介面程式設計是編碼中的一種設計思想,這種方式基於介面而不是固定的類別來建立應用程式。

如果您是一名程式設計師,那麼您可能聽說過則這樣的說法,例如:面向介面程式設計、使用抽象類別代替固定類別等等。

這些都是說的同一件事,編寫應用程式程式碼時,使其依賴抽象介面而不是具體的類別。

為什麼?

這是我第一次聽到這句話時的確切反應。為什麼要使用介面而不是類別?即使創建了接口,我也需要創建一個實現該接口的類別。這不是浪費時間嗎?

當然不是! !

這個世界上唯一不變的就是變化本身,也就是說,變化是永恆的。

就程式設計而言,這同樣沒有例外。業務需求隨著時間變化,我們的程式碼也要隨之變化。

所以程式碼必須保持靈活。

面向介面程式設計可以使程式碼鬆散耦合且靈活。

怎麼做?

觀察下面的程式碼。

class Logger {
    public function log($content) 
    {
        //日志保存到文件中.
        echo "Log to file";
    }
}

這是一個將日誌記錄到檔案的簡單類別。我們可以在控制器中呼叫它。

class LogController extends Controller
{
    public function log()
    {
        $logger = new Logger;
        $logger->log('Log this');
    }
}

但如果需要將日誌記錄到多個地方 (如資料庫,文件,雲端等) 時,我們又該怎麼辦呢。

然後我們可以更改 LogController 和 Logger 類別以適應這些變更。

class Logger {
    public function logToDb($content) 
    {
        //将日志记录到 db.
    }
    public function logToFile($content) 
    {
        //将日志保存到 file.
    }
    public function logToCloud($content) 
    {
        //将日志存储到 cloud.
    }
}
class LogController extends Controller
{
    public function log()
    {
        $logger = new Logger;
        $target = config('log.target');
        if ($target == 'db') {
            $logger->logToDb($content);
        } elseif ($target == 'file') {
            $logger->logToFile($content);
        } else {
            $logger->logToCloud($content);
        }
    }
}

現在我們可以記錄不同的目標了。但是,如果我們想將其他目標 (例如日誌) 新增到 redis 伺服器,該怎麼辦?最後,我們將同時修改 Logger 類別和 LogController 類別。

如您所見,這很快就擺脫了我們的控制,程式碼變得混亂。 Logger 類別很快就成為一個整體。這是一場惡夢。

因此,我們需要拆分事物。遵循 SOLID 原則,我們可以將職責移至對應的類別。

class DBLogger
{
    public function log()
    {
        //将日志记录到 db
    }
}
class FileLogger
{
    public function log()
    {
        //将日志保存到 file
    }
}
class CloudLogger
{
    public function log()
    {
        //将日志存储到 cloud
    }
}

並且控制器更改為:

class LogController extends Controller
{
    public function log()
    {
        $target = config('log.target');
        if ($target == 'db') {
            (new DBLogger)->log($content);
        } elseif ($target == 'file') {
            (new FileLogger)->log($content);
        } else {
            (new CloudLogger)->log($content);
        }
    }
}

這樣就好多了。現在如果要新增其他日誌記錄目標,我們可以建立一個新類別並將其新增至 Controller 中的 if-else。

但是,我們的控制器仍然負責選擇記錄器。對於控制器,不需要知道不同的記錄器並在它們之間進行選擇。它只需要一個有 log() 方法的記錄器類別來記錄內容。

使用介面

這種情況就適合使用介面。那什麼是介面?

介面是對物件可以執行的操作的描述。

以我們的範例為例,控制器只需要帶有 log() 方法的記錄器類別。因此,我們的介面必須描述它必須具有 log() 方法。

interface Logger
{
    public function log($content);
}

如您所見,它僅包含函數聲明,而不包含其實現,這就是為什麼將其稱為抽象的原因。

實作介面時,實作介面的類別必須提供介面中定義的抽象方法的實作細節。

在我們的範例中,任何實作 Logger 介面的類別都必須提供抽象方法 log () 的實作細節。

然後,我們可以在控制器中註入此介面。

class LogController extends Controller
{
    public function log(Logger $logger)
    {
        $logger->log($content);
    }
}

現在,控制器不再關心傳遞給它的記錄器類型。它需要知道的是它必須實作 Logger 介面。

因此,我們需要修改 Logger 類別以實作此介面。

class DBLogger implements Logger
{
    public function log()
    {
        //将日志记录到 db
    }
}
class FileLogger implements Logger
{
    public function log()
    {
        //将日志存储到 file
    }
}
class CloudLogger implements Logger
{
    public function log()
    {
        //将日志保存到 cloud
    }
}

現在,我們可以新增更多記錄器,而無需觸及現有程式碼。我們要做的就是建立一個實作 Logger 介面的新類別。

class RedisLogger implements Logger
{
    public function log()
    {
        //将日志存储到 redis
    }
}

我們的程式碼現在看起來變得靈活,低耦合了,我們可以隨時改變實作方式而不用去改變之前的程式碼。

依賴注入

當我們使用的是 Laravel 框架,我們可以使用服務容器去自動註冊介面的實作。

因為 Laravel 提供開箱即用的方法注入,所以我們只需要把介面和實作綁定起來。

首先我們需要建立一個 logger 的設定檔。就像這樣

<?php
return [
    &#39;default&#39; => env(&#39;LOG_TARGET&#39;, &#39;file&#39;),
    &#39;file&#39; => [
        &#39;class&#39; => App\Log\FileLogger::class,
    ],
    &#39;db&#39; => [
        &#39;class&#39; => App\Log\DBLogger::class,
    ],
    &#39;redis&#39; => [
        &#39;class&#39; => App\Log\RedisLogger::class,
    ]
];

然後在app/Providers 路徑下AppServiceProvider.php 的檔案加入如下程式碼

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $default = config(&#39;log.default&#39;);
        $logger = config("log.{$default}.class");
        $this->app->bind(
            App\Contracts\Logger::class, // the logger interface
            $logger
        );
    }
}

這樣做的效果是,從logger.php 設定檔讀取預設的logger ,然後綁定到Logger interface 。這樣當我們使用 Logger interface ,容器將會幫我們解析並傳回預設的 Logger 實例。

因為預設的 logger 是使用 env() 助手指定的,所以我們可以在不同的環境中使用不同的 logger ,例如本地環境使用 file ,生產環境使用 db 。

總結

使用介面可以讓我們寫出低耦合的程式碼,提供一個抽象層。它允許我們隨時改變實現方式。所以,盡可能將你的應用程式中可變的部分用面向介面的方式實現。

推薦教學:《PHP教學

以上是Laravel 面向介面程式設計(實作)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除