跟控制器说再见吧,从今天开始使用请求处理器(Request Handlers) 范式

在过去几年中, 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 开发者社区 https://laravel-china.org/top…

Leave a Reply

Your email address will not be published. Required fields are marked *