php面向接口编程思想,面向接口编程 - 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 的配置文件。 就像這樣
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 。
總結
使用接口可以讓我們寫出低耦合的代碼,提供一個抽象層。它允許我們隨時改變實現方式。所以,盡可能將你的應用中可變的部分用面向接口的方式實現。
感謝閱讀
本文中的所有譯文僅用于學習和交流目的,轉載請務必注明文章譯者、出處、和本文鏈接
我們的翻譯工作遵照 CC 協議,如果我們的工作有侵犯到您的權益,請及時聯系我們。
總結
以上是生活随笔為你收集整理的php面向接口编程思想,面向接口编程 - Laravel 实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php $y=$x^$y;,PHP语法
- 下一篇: php fsockopen socket