我应该在控制器或帮助器中保留更多代码吗?

时间:2017-02-02 10:15:49

标签: ruby-on-rails

在我的项目中,我有一些场景,在控制器操作中我需要做很多依赖的东西,这些东西无法直接移动到模型中。为了保持控制器的瘦,我最终将该代码移动到帮助器。这是一个好习惯吗?

例如,在控制器操作中,我需要先验证请求的校验和,然后在两个表中进行输入,然后调用一些外部api并根据结果更新一些值。我最终将调用外部api转移给了帮助者。

2 个答案:

答案 0 :(得分:2)

  

这是一个好习惯吗?

<强>都能跟得上即可。助手仅适用于特定于视图的内容。货币格式,日期格式,包装特定<div>中的东西,这种东西。默认情况下,它们甚至不能在控制器中使用。你必须明确要求它们。

协调多个运动部件的合作不应该是帮手。是的,控制器也不是一个好地方。我,我通常将这种逻辑放在ServiceObject或其他东西中。所以我的控制器通常看起来像这样:

class ProjectsController < AppicationController
  def create
    # this will create the project, create associated objects, push notifications to 
    # whatever needs to be notified, etc. 
    ::ServiceObjects::Project::Create.new(project_params)
    # render something
  end

  ...
end

这样,您的控制器仍然很瘦。另外,服务对象更容易测试。

答案 1 :(得分:1)

良好的做法?当然不!它不仅会使代码维护成为一项艰巨的任务,而且还会使测试变得困难。

从来没有一种情况下无法从控制器移动逻辑。通常,对于任何面向对象的语言,项目应至少基于MVC模式,以将业务逻辑与表示分开。

根据您的示例,简单的事情可以轻松地将个人责任委派给专家类,从而从控制器中删除逻辑。

class FooController < ApplicationController
  def create 
    handler = FooHandler.new(params[:foos])
    foo = handler.process_foo

    if foo[:result]
      flash[:success] = 'Foo was successful'
      redirect_to foo_path
    else
      flash[:error] = foo[:errors]
      redirect_to foo_path
    end
  end
end

class FooHandler
  delegate :valid_checksum?, to: :checksum_klass
  delegate :create_foos, to: :foo_klass
  delegate :call_foo_api, to: :foo_api_klass

  def initialize(params)
    @params = params
  end

  def process_foo
    return {result: false, errors: 'failed checksum'} unless valiid_checksum?
    return {result: false, errors: 'failed to create the foos' unless create_foos
    return {result: false, errors: 'api errors'} unless call_foo_api
    {result: true}
  end

  private
  attr_accessor :params

  def checksum_klass
    @checksum_klass ||= ChecksumChecker.new(params[:checksum])
  end

  def checksum_klass
    @foo_klass ||= FooCreator.new(params[:foo_objects])
  end

  def checksum_klass
    @foo_api_klass ||= FooApiHandler.new(params[:foo_objects])
  end
end

上述实现方式是一个示例,说明如何开始将每个单独的进程拆分为自己的类,该类运行单个FooHandler类,将所有逻辑与控制器分离。在这个例子中,所有控制器都关心的是该过程是否成功。

我的示例基于每个动作都包含在其自己的类中,因此FooHandler可以在需要时将责任委派给该类,传递一些数据并清洗任何责任。它所关心的只是结果。

这一切都归结为你对模式的理解。我使用的两个最有用的模式(除了Rubys duck typing和delegation)是观察者模式和装饰模式。

观察者模式允许您将依赖进程与可观察类的状态相关联。因此,对于您的示例,如果API调用依赖于正在创建的对象,请尝试设置具有FooApiHandler作为订阅侦听器的FooCreationHandler。如果处理程序成功创建记录,则它可以通知FooApiHandler可以调用外部API,从而解除依赖关系。

装饰器之类的模式允许您在需要时“装饰”具有特定行为的对象,而不是对其他类具有大的,错综复杂的if语句和“知识”。同样,这是一个很好的模式,允许您使用抽象行为创建专家类,而不是包含一个类或控制器的逻辑。

希望这个简短的例子有所帮助。

相关问题