面向介面程式設計是編碼中的一種設計思想,這種方式基於介面而不是固定的類別來建立應用程式。
如果您是一名程式設計師,那麼您可能聽說過則這樣的說法,例如:面向介面程式設計、使用抽象類別代替固定類別等等。
這些都是說的同一件事,編寫應用程式程式碼時,使其依賴抽象介面而不是具體的類別。
為什麼?
這是我第一次聽到這句話時的確切反應。為什麼要使用介面而不是類別?即使創建了接口,我也需要創建一個實現該接口的類別。這不是浪費時間嗎?
當然不是! !
這個世界上唯一不變的就是變化本身,也就是說,變化是永恆的。
就程式設計而言,這同樣沒有例外。業務需求隨著時間變化,我們的程式碼也要隨之變化。
所以程式碼必須保持靈活。
面向介面程式設計可以使程式碼鬆散耦合且靈活。
怎麼做?
觀察下面的程式碼。
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 [ 'default' => env('LOG_TARGET', 'file'), 'file' => [ 'class' => App\Log\FileLogger::class, ], 'db' => [ 'class' => App\Log\DBLogger::class, ], 'redis' => [ 'class' => App\Log\RedisLogger::class, ] ];
然後在app/Providers 路徑下AppServiceProvider.php 的檔案加入如下程式碼
class AppServiceProvider extends ServiceProvider { public function register() { $default = config('log.default'); $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中文網其他相關文章!