Whip Monstrous Code Into Shape 09-Consider Strategizing

Tutorials often illustrate the simplest possible path through your codebase to demonstrate a particular concept. But the truth is that, in real life, things can quickly become quite a bit more complicated. For example, imagine that your website allows users to sign up for subscriptions. The only problem is that, based upon the form request data, you'll need to execute this process in multiple ways. Sounds like you're going to have lots of conditionals in your controller, right? Well, maybe there's another option: strategies.


这个视频主要讲的是如何在控制器中使用 strategies (策略)。

通常在教程中演示的代码都是一些比较简单的情况,比如注册一个用户,只需要 User::create 就行。但是在实际的生产中,有些情况就会变得比较复杂。比如用户订阅一个产品,有可能有以下几种不同的情况:

  • 免费

  • monthly 按月付费

  • yearly 年费

  • team member access 企业用户

  • foreave account 永久账户

这就意味着你可能需要在控制器中通过不同的 request 参数来做不同的处理:

class SubscriptionsController
{
    public function store(Request $request)
    {
        if ($request->plan == 'free') {
            // do something
        }

        if ($request->plan == 'forever') {
            // do something
        }

        // ...
    }
}

当你在控制器中看到这种情形时,不妨考虑使用 strategies (策略)。

Jeff 给出了一个简单的步骤,你可以按照这四步来使用 strategies (策略):

  1. Identify a point of flexbility;
  2. Extract each strategy into its own class;
  3. Ensure that each of those strategies adheres to a common contract/interface;
  4. Determine the proper strategy, and let it handle the task;

我英语不行,根据我的理解大概翻译一下:

  1. 列出不同的策略(跟直接翻译差异挺大,但是应该意思是这个);
  2. 把每个策略提取到自己单独的类中;
  3. 确保不同的策略实现了相同的契约/接口;
  4. 解析正确的策略,然后处理逻辑;

我们按照以上的四个步骤来重构这个方法。

首先,列出不同的策略,在我们的例子中,就是需要处理不同的五种情形:

  1. Forum Account;
  2. Monthly Account;
  3. Yearly Account;
  4. Team Member Access;
  5. Forever Account

第二步,把每个策略提取到单独的类中,于是我们创建以下几个类,每个类都代表一种不同的策略:

class RegistersForumUser
{

}

class RegistersMonthlyUser
{

}

class RegistersYearlyUser
{

}

class RegistersTeamMemberUser
{

}

class RegistersLifeTimeUser
{

}

第三步,确保不同的策略实现了相同的契约/接口,我们可以创建一个 interface 或者 抽象类,然后这五个类都实现接口或者继承抽象类

interface RegistersUser{
    public function handle();
}

class RegistersForumUser implements RegistersUser
{
    public function handle()
    {
        // TODO: Implement handle() method.
    }
}

class RegistersMonthlyUser implements RegistersUser
{

    public function handle()
    {
        // TODO: Implement handle() method.
    }
}

class RegistersYearlyUser implements RegistersUser
{

    public function handle()
    {
        // TODO: Implement handle() method.
    }
}

class RegistersTeamMemberUser implements RegistersUser
{

    public function handle()
    {
        // TODO: Implement handle() method.
    }
}

class RegistersLifeTimeUser implements RegistersUser
{

    public function handle()
    {
        // TODO: Implement handle() method.
    }
}

第四步,解析正确的策略,然后处理逻辑。

其实写到这里,估计大家都看出来了,这就是经常用到的工厂模式。你可以创建一个工厂类来解析正确的策略,如果逻辑简单,也可以直接在控制器中解析:

protected function getRegistrationStrategy(Request $request)
{
    if ($request->plan == 'free') {
        return new RegistersForumUser();
    }

    if ($request->plan == 'monthly') {
        return new RegistersMonthlyUser();
    }
    if ($request->plan == 'yearly') {
        return new RegistersYearlyUser();
    }

    if ($request->plan == 'team') {
        return new RegistersTeamMemberUser();
    }

    if ($request->plan == 'forever') {
        return new RegistersLifeTimeUser();
    }
}

当然你也可以用 switch case 来判断正确的策略。这个时候在 store 方法里面做的就是获取正确的策略然后使用相应的策略处理:

public function store(Request $request)
{
    $strategy = $this->getRegistrationStrategy($request);
    $strategy->handle();
    // ...
}

使用这种方式扩展性好,条理清晰。

从第一节视频看到这里,其实可以发现都是一些非常简单的技术,不涉及到任何高深莫测的东西,但是对于代码的整洁,优雅来说有很大的提高,也不局限在 Laravel 或 php,所有的语言都不出其中。

本帖已被设为精华帖!
本帖由 Summer 于 7年前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 3
GalaxyNo_1

:+1:

7年前 评论

感觉有点过度了没

7年前 评论

@wangat 可能例子比较简单,你可以考虑在比较复杂的情况下用。代码解耦、易维护带来的是复杂度肯定会一定程度上有所增加,另外如果担心类多了对象的创建管理有性能上的问问题的话,我觉得相对网络连接的耗时这点可以完全忽略不计,对象的创建是在堆上,速度很快的

7年前 评论

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