Whip Monstrous Code Into Shape - 08 Consider Splitting Tasks into Steps


What do you do in the situations where a particular task consists of a dozen different unique steps? Well we've already reviewed a number of options in this series, including use-cases and events; however, another perfectly acceptable route is to extract each step into its own class, and then filter through an array of these bite-sized classes and trigger them. I'll show you how.

之前的视频介绍了通过事件(event)和用例(use case)来处理某个有很多不同步骤的情况,这个视频介绍的是另外一个方法来处理这种情况,就是把这些步骤提取到一个单独的类,然后保存到数组中,通过循环调用这些类来分别处理。

假如有个 Thing 类, handle() 里面需要处理 5个不同的步骤,甚至更多。如果把这些逻辑处理都放在一起的话,毫无疑问会使 Thing 类很臃肿,并且逻辑不清楚。

class Thing
{
    public function handle()
    {
        // step1: DoThis
        // step2: DoThat
        // step3: RunSomething
        // step4: EraseSomethingElse
        // step5: AddSomethingToAnother
        // ...
    }
}

这个时候可以把这些步骤从方法里面提取出来,分别创建自己的类,并定义一个相同的方法处理。

class DoThis {
    public function handle()
    {
        // ...
    }
}

class DoThat {
    public function handle()
    {
        // ...
    }
}

class RunSomething {
    public function handle()
    {
        // ...
    }
}

class EraseSomethingElse {
    public function handle()
    {
        // ...
    }
}

class AddSomethingToAnother {
    public function handle()
    {
        // ...
    }
}

接下来在需要调用的方法里面创建一个 “tasks” 数组,然后使用 foreach 循环并实例化,调用 handle() 方法。这就是为什么不同的 task 需要定义同一个 handle() 方法,当然你也可以按照实际情况定义不同的方法,主要是要相同,方便后面用循环调用。

我记得论坛里面有个小伙伴问了为什么 Laravel 里面很多都定义 handle 方法,很大一个原因就是为了方便调用而已。

class Thing
{
    public function handle()
    {
        $tasks = [
            DoThis::class,
            DoThat::class,
            RunSomething::class,
            EraseSomethingElse::class,
            AddSomethingToAnother::class
        ];

        foreach ($tasks as $task) {
            (new $task)->handle();
        }
    }
}

相比事件和用例来说,我觉得这种方法可能看起来比较明了一点,不用追踪到用例或者事件注册中才知道处理的流程,在调用的时候就可以一目了然。

在 Laravel 里面有个很典型的例子就是用的这种方法来处理。

Illuminate\Foundation\Http\Kernel 中当接收到 index.php 里面 handle 的请求时会调用 bootstrap() 方法来初始化 App。

public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }
}

$this->bootstrappers() 返回的就是类似于上面例子里面的 tasks 数组:

protected $bootstrappers = [
    'Illuminate\Foundation\Bootstrap\DetectEnvironment',    // 设置 app 运行环境
    'Illuminate\Foundation\Bootstrap\LoadConfiguration',    // 加载配置
    'Illuminate\Foundation\Bootstrap\ConfigureLogging',     // 配置 logger
    'Illuminate\Foundation\Bootstrap\HandleExceptions',     // 配置 exception handler
    'Illuminate\Foundation\Bootstrap\RegisterFacades',      // 注册 facades
    'Illuminate\Foundation\Bootstrap\RegisterProviders',    // 注册所有的 ServiceProvider
    'Illuminate\Foundation\Bootstrap\BootProviders',        // boot 所有的 ServiceProvider
];

看看容器的 bootstrapWith 方法:

public function bootstrapWith(array $bootstrappers)
{
    $this->hasBeenBootstrapped = true;

    foreach ($bootstrappers as $bootstrapper) {
        $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

        $this->make($bootstrapper)->bootstrap($this);

        $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
    }
}

把触发事件这些丢开,主要的就是foreach 循环,创建相应的实例并调用 bootstrap() 方法:

foreach ($bootstrappers as $bootstrapper) {
    $this->make($bootstrapper)->bootstrap($this);
}

$bootstrappers 数组中的类都实现了 bootstrap 方法。

本帖已被设为精华帖!
本帖由 Summer 于 7年前 加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 8
Summer

点个赞加个精再看

7年前 评论

@Summer summer哥我想说写到这里我不想写了吗?可是开始做了又不想放弃啊 。。感觉都是比较简单的东西,但是表达出来很不习惯,我很佩服你写了书。。20节,我要坚持下去。:facepunch:

7年前 评论
Summer

必须坚持,这些东西不简单 :facepunch:

7年前 评论

@oustn 麻烦开通一下打赏功能好吗?谢谢。

7年前 评论

@oustn 看起来简单,但是大部分人都不会,而且这些东西虽然代码不多,但却是很多优秀的经验积累的成果,所以这些东西并不是“简单的东西”,而是精华。

7年前 评论
Lonexw

每篇都有在认真看的

7年前 评论

@overtrue 超哥说的很对,laracast 的视频很多,但是我觉得这 20 节是特别好的东西,不是从技术层面,而是更上一层。我觉得跨过这一层可能就是码农和工程师的区别了

7年前 评论

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