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 方法。
推荐文章: