为什么我们需要use_cases

栏目: Ruby · 发布时间: 5年前

内容简介:我一直以为,在写rails的时候使用事情的起因是因为一次试想我们有一个表单,需要给用户发验证码,并且在用户输入验证码以后比对验证码,才能允许用户正常提交。假设这个方法叫做

我一直以为,在写rails的时候使用 use_case 是一种很常见的意识。但是我发现还是有一部分人从来没用过或者没听说过 use_case ,出于让自己也更仔细的思考这种用法的目的,记录一下。

事情的起因是因为一次 code review , 我很庆幸我们团队内部的 code review 做的一直都不错,大家都很认真的给彼此的代码做认真的检查,并且都能从中学到很多东西。 我封装了一个use_case,并且在model的callback里面去调用了这个use_case。这个做法遭到了给我做code review同事的质疑,然后我才知道原来还有部分道友还不知道use_case的用法。 当然我也并不是说我这样的做法就是最好的,但是这种既不属于model,也很难成为一个公用lib class的,并且单一职责,逻辑简单的玩意儿,随着项目的成长随时可能改变的东西,封装为一个use_case绝对是一个不错的选择。

试想我们有一个表单,需要给用户发验证码,并且在用户输入验证码以后比对验证码,才能允许用户正常提交。假设这个方法叫做 send_code ,我们创建一个发验证码的module叫做 Postman

我们的发验证码的controller长这样:

class AuthController < ApplicationController
  def create
    auth_code = AuthCode.create(sms_params)
    if svc.success?
      render json: { sid: auth_code.sid }
    else
      render json: { status: 500 }, status: 500
    end
  end

  private
  def sms_params
    params.require(:verification).permit(:phone_number, :code)
  end
end

model长这样:

class AuthCode < ActiveRecord::Base
  before_create :generate_code
  after_create :send_code

  def send_code
    Postman.new(phone_number, verify_code).perform
  end

  private
  def generate_code
    self.verify_code = rand.to_s[2..5].to_s
  end
end

这样我的controller只用关心验证码的创建就好,发不发/怎么发都是model的事儿, AuthCode 记录创建好以后会通过callback去发送,这里的 Postman 就是我们前面提到的一个 use_case

class Postman
  include UseCase

  def initialize(phone_number, message)
    @phone_number = phone_number
    @message      = message
  end

  def perform
    SMS::Client.new.messages.create(
      to: @phone_number,
      body: @message
    )
  end
end

UseCase 可以理解为一个接口定义,描述了所有use_case都需要遵循的规则,便于维护和自描述:

module UseCase
  extend ActiveSupport::Concern
  include ActiveModel::Validations
  # extented class should contains these methods
  module ClassMethods
    # The perform method of a UseCase should always return itself
    def perform(*args)
      new(*args).tap(&:perform)
    end
  end

  # implement all the steps required to complete this use case
  def perform
    raise NotImplementedError
  end

  # inside of perform, add errors if the use case did not succeed
  def success?
    errors.none?
  end
end

这里有几点需要注意的:

  • 每个use_case只有一个public的实例方法 ( perform )
  • 每个use_case只有一个职责,也就是只做一件事(发短信)
  • 因为上一条,所以测试也很容易写
  • 有一个Gem叫做 caze 可以让use_case的调用更优雅一些,不必像我这样显式的调用:
    Postman.new(phone_number, verify_code).perform

    具体可以看看 caze 的文档。

顺便发一下代码的结构:

├── app
│   ├── controllers
│   │   ├── application_controller.rb
│   │   ├── auth_controller.rb
│   │   └── users
│   ├── models
│   │   ├── user.rb
│   │   └── auth_code.rb
│   ├── use_cases
│   │   ├── postman.rb
│   │   └── use_case.rb

这样一来,我们就:

把程序有什么

models 比如 user , auth_code 等等不怎么变化很稳定的东西。

和程序做什么

业务逻辑和 use cases 会随着项目的成长不停改变。

分开了。

那问题来了, 我们什么时候需要 use_case 呢?

当你需要管理resources,你就必须用rails的方式,也就是面向resource的方式去做,model啦,presenter啦,抽象类啦,都是很方便且有道理的。 但是,当你的业务里面有一个很特殊的功能,它不属于你的主营业务逻辑,且职责单一,随时可以变化甚至移除,那么你就可以考虑使用 use_case ,就像上面的代码那样。

总结

一万个 程序员 眼里有一万种 最佳实践 ,use_case不是所有的场景都适用。但是当你有这样的思维以后,对于如何组织你的use_case,如何保持代码结构整洁肯定是有一定帮助的。

Referreces


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

B2B品牌管理

B2B品牌管理

(美)菲利普·科特勒、(德)弗沃德 / 楼尊 / 2008-1 / 35.00元

《B2B品牌管理》是第一本专门系统地阐述B2B品牌化的专业书籍,由营销大师菲利普•科特勒与弗沃德教授合作而成。他们以非凡的智慧和深厚的经验告诫B2B企业如何运用目标明确、重点突出的品牌化战略取得市场竞争优势地位,从而更加接近顾客,也更接近成功。在众多关于品牌的书籍中,《B2B品牌管理》的独特价值在于其根据实际环境探讨B2B品牌和品牌化问题,重点介绍和分析前沿的思想和最佳实践;通过与B2C企业的品牌......一起来看看 《B2B品牌管理》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具