Whip Monstrous Code Into Shape - 02 Use Cases

file

A use case is nothing more than a description of how a user should interact with a particular part of your system. Traditionally, this description may be written on something as simple as an index card. However, what if we applied this approach to the code, itself? What might that look like?

我们通常用用例来描述一个比较复杂的任务,通常都是用一些描述性的语言或者 UML 来表述。Jeffrey 在这个视频中展现了如何在开发中使用 use case 来更好的组织我们的代码。

这个视频中有个简单的场景,比如用户购买一个播客(Podcast),在处理这个请求时需要很多不同的操作。如果我们简单的把这些处理逻辑写在控制器中,毫无疑问会导致控制器臃肿及难以维护。

class PurchaseController extends Controller
{
    public function store()
    {
        // step1 ....
        // step2 ....
        // step3 ....
        // step4 ....
    }
}

这个时候我们可以为购买播客这个任务建立一个 UseCase 来处理,在视频中 Jeffrey 提到了一个很重要的问题,那就是 Usecase 的命名,虽然如何命名虽然对于功能来说没有任何问题,但是对于理解和维护代码来说非常重要。比如在这个例子中,他把用例命名成 PurchasePodcast,一目了然并且容易理解。

class PurchaseController extends Controller
{
    public function store()
    {
        /**
         * 购买播客use case
         */
        PurchasePodcast::perform(/**参数*/);

        return redirect(/*any thing...*/);
    }
}

PurchasePodcast 中就是我们定义逻辑的部分。

class PurchasePodcast
{

    public static function perform(/**参数*/)
    {
        return (new static(/**参数*/))->handle();
    }

    private function handle()
    {
        $this->preparePurchase()
            /** other method ... */
            ->sendEmail();
    }

    private function preparePurchase()
    {
        // ...
        return $this;
    }

    private function sendEmail()
    {
        // ...
        return $this;
    }
}

通过使用静态方法避免在调用时需要显式的创建一个实例,然后通过return $this 来实现链式调用。看看handle方法里面的调用,是不是很像我们写的一个用例?首先....,然后....,再次.....,最后.....,结构清晰,容易理解,就算过了半年你忘记了 PurchasePodcast 的处理流程,看看 handle 方法里面的调用就能很容易明白。

如果在开发的过程中用了大量类似的方式,你也可以把部分代码分离出一个抽象的UseCase类,然后所有的use case 都继承这个类,实现handle 方法。

abstract class UseCase
{
    public static function perform(/**参数*/)
    {
        return (new static(/**参数*/))->handle();
    }

    abstract protected function handle();
}

其实在 Laravel 中可以直接使用 job 来实现。我们通常说起 job,更多的是想到队列,不使用队列的job,跟我们自己写的 use case 本质上都是一样,只不过在控制器中可以直接用 dispatch 分发 :dispatch(new PurchasePodcast());

class PurchaseController/** implements ShouldQueue */
{
    /**  use InteractsWithQueue, Queueable, SerializesModels; */

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $this->preparePurchase()
            /** other method ... */
            ->sendEmail();
    }

    private function preparePurchase()
    {
        // ...
        return $this;
    }

    private function sendEmail()
    {
        // ...
        return $this;
    }
}

这种方法当然也有缺陷,不太好单元测试,相对的集成测试更容易。

本帖已被设为精华帖!
本帖由 Summer 于 7年前 加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 2

laracasts的PHP视频非常棒

7年前 评论

数据库事务处理和第三方服务方面,其中一环出错了怎么处理?

7年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!