跟控制器說再見吧,從今天開始使用請求處理器(Request Handlers) 正規化

Charlie_Jade發表於2019-01-07

在過去幾年中, PHP 開發環境發生了很大的變化。我們開始使用更多更好的設計模式,比如 DRYSOLID 設計模式原則。但為什麼我們仍然在使用控制器?

如果您以前曾經參與過大型專案的架構編寫,那麼您可能已經注意到遲早會出現控制器過多的這種現象。即使您將控制器邏輯分離到各種類庫或服務類中,大量的依賴項和方法以及程式碼的行數還是會隨著時間的推移不斷增長。

我來介紹一下請求處理器。這個概念很簡單,但很多 PHP 開發人員都不知道。請求處理器可以理解為僅包含單個動作(Action)的控制器,能夠使請求到響應的流程更加清晰明確。這個概念與 Paul M. Jones 提出的 Action-Domain-Responder 設計模式有相似之處,後者是MVC模式的替代品。

file

一個好的方法去建立請求處理器就是使用呼叫類。可呼叫類是使用PHP中的魔術方法 __invoke ,把他們變成一個 Callable ,這將允許他們作為函式呼叫。這裡有一個關於呼叫類的簡單例子:

class Greeting
{
    public function __invoke($name)
    {
        echo 'Hello ' . $name;
    }
}

$welcome = new Greeting();
$welcome('John Doe'); //輸出 Hello John Doe
複製程式碼

看到這裡你大概會想;“我為什麼要這樣做?”。我知道這是一個有點荒謬的例子。但是它與某些程式碼一起使用時例如可呼叫物件和依賴注入,它將變得很有意義。一個好的使用例子是路由的請求處理在Laravel和Slim框架中。

Route::get('/{name}', Greeting::class);
複製程式碼

是否讓你大吃一驚?沒有?讓我們把它和你通常寫的比較一下:

Route::get('/{name}', 'SomeController@greeting');
複製程式碼

還沒有?除了程式碼好看之外,還有其他優點。讓我們先去看看使用請求處理程式比控制器有那些優點。

單一模式

SOLID 的第一個原則是“單一模式”。在我看來,控制器中存在許多的方法,就打破了這個原則。請求處理程式提供了一個很好的解決方案,可以將這些操作分成它們自己的類,使它們更易於維護,重構和測試。

這是從 UsersController 中提取的2個請求處理程式的示例,它處理使用者配置檔案的編輯和儲存:

class EditUserHandler
{
    public function __construct(
        UserRepository $repository,
        Twig $twig
    ) {
        ...
    }

    public function __invoke(Request $request, Response $response)
    {
        ...
    }
}

class UpdateUserHandler
{
    public function __construct(
        UserRepository $repository,
        UpdateUserValidator $validator,
        ImageManager $resizer,
        Filesystem $storage
    ) {
        ...
    }

    public function __invoke(Request $request, Response $response)
    {
        ...
    }
}
複製程式碼

接下來讓我們看下一個優勢;

測試效能

你最近有沒有為你的專案編寫過單元測試?在編寫單元測試的時候你可能編寫了一些與測試無關的模擬依賴項。由於請求處理器將不同的控制器操作拆分為單獨的類,因此您只需注入或繫結該動作所需要的依賴項即可。

這是 Jeffrey Way 的一些建議 Twitter

提示:讓你的功能測試儘可能更加詳細具體,使用測試用例來描述重要的規則和能力。

這基本不會讓你的請求處理器都有一個測試檔案。對於那些繁瑣的控制器測試檔案來說是一個非常好的改進。

重構

PhpStorm 和其他的編輯器都有強大的程式碼重構功能,但是如果你使用的是 Laravel 或者 Slim 框架預設的路由方法將控制器繫結到路由,那麼你可能會遇到這種問題。

例如重新命名:

Route::get('/{name}', Greeting::class);
複製程式碼

比這簡單得很多:

Route::get('/{name}', 'SomeController@greeting');
複製程式碼

結論

請求處理器是控制器很好的替代品。控制器的動作(Actions)被分為多個獨立的請求處理器類,分別負責響應單一的動作。這使整個專案的程式碼更易於維護、重構和測試。

您是否應當使用請求處理器替換所有控制器?可能不是。對於小型應用程式而言,為了簡單,將動作組合成控制器或許更加合理。當我開始在 Teamleader 工作後,我才開始發掘請求處理器,我覺得近期沒什麼換回控制器的必要了。

如果有什麼不清楚或有疑問,請在下面留下評論告訴我,我會更新這篇文章。

轉自 PHP / Laravel 開發者社群 laravel-china.org/topics/2194…

相關文章