【问题标题】:How would you tidy up this controller logic?你将如何整理这个控制器逻辑?
【发布时间】:2009-01-23 12:39:29
【问题描述】:

如果满足某些条件,我在控制器中有一些逻辑可以设置对象的状态:

if params[:concept][:consulted_legal] == 0 && params[:concept][:consulted_marketing] == 1
  @concept.attributes = {:status => 'Awaiting Compliance Approval'}
elsif params[:concept][:consulted_marketing] == 0 && params[:concept][:consulted_legal] == 1 
  @concept.attributes = {:status => 'Awaiting Marketing Approval'}
elsif params[:concept][:consulted_marketing] == 0 && params[:concept][:consulted_legal] == 0
  @concept.attributes = {:status => 'Awaiting Marketing & Legal Approval'}
else
  @concept.attributes = {:status => 'Pending Approval'}
end

我在创建和更新操作之间共享。你将如何重构这种肮脏的东西?寻找最佳做法。

编程新手,热衷于清理我的代码。

TIA。

【问题讨论】:

    标签: ruby-on-rails ruby refactoring controller


    【解决方案1】:

    您可以减少代码对这两个条件的依赖,并使其更加灵活。

    waiting_on = []
    waiting_on << 'Compliance' unless params[:concept][:consulted_marketing]
    waiting_on << 'Legal' unless params[:concept][:consulted_legal]
    status = waiting_on.empty? ? "Awaiting #{waiting_on.join(' & ')} Approval" : 'Pending Approval'
    @concept.attributes = {:status => status}
    

    不带过滤器的创建和更新版本:

    def create
      set_concept_status_attribute
      ...
    end
    
    def update
      set_concept_status_attribute
      ...
    end
    
    private
      def set_concept_status_attribute
        waiting_on = []
        waiting_on << 'Compliance' unless params[:concept][:consulted_marketing]
        waiting_on << 'Legal' unless params[:concept][:consulted_legal]
        status = waiting_on.empty? ? "Awaiting #{waiting_on.join(' & ')} Approval" : 'Pending Approval'
        @concept.attributes = {:status => status}
      end
    

    或者使用 before_filter:

    before_filter :set_concept_status_attribute, :only => [:create, :update]
    
    def create
      ...
    end
    
    def update
      ...
    end
    

    如果你可以把它移到你的视图中,它看起来会更好:

    module ConceptHelper
      def get_concept_status
        ...
      end
    end
    
    <%= get_concept_status %>
    

    【讨论】:

    • 感谢塞缪尔。我同意,它是这里最可重复使用和最干净的。
    • 出于好奇,因为我两次使用这个冗长的业务规则 - 在创建和更新中,您是否建议将其放入一个辅助方法中,并与这两种方法共享?如果是这样,我会在哪里打包?
    • 我同意 jon 的观点,即这应该在模型中,因为它是 BL。但是如果你想在控制器中使用它,你可以把它放在一个帮助器或过滤器之前。你也可以把它放到一个视图中。
    • 再次感谢:喜欢这里用于制作辅助方法的代码。我已经就“转向模型”问题提出了一个单独的问题,我还没有设法开始工作,您可能有时间也可能没有时间查看。在stackoverflow.com/questions/473325/…
    【解决方案2】:

    这是我的看法。我称之为 Super DRY。

    statuses = 
      [
        ['Awaiting Marketing & Legal Approval','Awaiting Compliance Approval'],
        ['Awaiting Marketing Approval','Pending Approval']
      ]
    
    {:status => statuses[params[:concept][:consulted_legal].to_i][params[:concept][:consulted_marketing].to_i]}
    

    或者,一种更传统的方法——冗长但可读:

    status = if params[:concept][:consulted_legal] == "0"
      if params[:concept][:consulted_marketing] == "1"
        'Awaiting Compliance Approval'
      else
        'Awaiting Marketing & Legal Approval'
      end
    else
      if params[:concept][:consulted_marketing] == "0"
        'Awaiting Marketing Approval'
      else
        'Pending Approval'
      end
    end
    
    @concept.attributes = {:status => status}
    

    注意:看起来您的原始代码正在检查复选框的值。 params 散列中的值是always Strings,而不是Fixnums,所以我的代码比较字符串。如果出于某种原因比较 Fixnums 是您的情况所需要的,只需去掉数字周围的引号即可。

    【讨论】:

    • 谢谢bjeanes。这也干净多了。
    • 不确定您是在谈论第一次还是第二次尝试。我的第二次尝试是列出的第一个尝试,并使用了类似真值表的方法。如果它们是谨慎的四个州,那绝对是最具扩展性的 IMO。
    【解决方案3】:

    这看起来是业务逻辑,所以它应该在模型中。

    您的模型可能需要几个属性:consulted_legal 和consulted_marketing,以及在其中任何一个发生更改时设置状态的方法,如下所示:

    before_update :set_status
    
    def set_status
      if consulted_legal && consulted_marketing
        status = "Pending Approval"
      elif consulted_legal && !consulted_marketing
        status = "Awaiting Marketing Approval"
      elif !consulted_legal && consulted_marketing
        status = "Awaiting Legal Approval"
      elif !consulted_legal && !consulted_marketing
        status "Awaiting Marketing & Legal Approval"
      end
    
      true # Needs to return true for the update to go through
    end
    

    【讨论】:

    • 谢谢。我想知道何时有人会说将其移入模型中,我喜欢将其作为解决方案。我要试试这个,看看结果如何。我有点担心您没有在此处的条件中检查 == "t" 或 "f":我应该这样做吗?
    • before_update 是否也适用于创建?还是我应该做一个 before_create_or_update?
    【解决方案4】:

    将其分解为嵌套的 if 语句。

    if params[:concept][:consulted_legal] == '0'
      if params[:concept][:consulted_marketing] == '1'
        @concept.attributes = { :status => 'Awaiting Compliance Approval' } 
      else
        @concept.attributes = { :status => 'Awaiting Marketing & Legal Approval' }
      end
    else
      if params[:consulted_marketing] == '1'
        @concept.attributes = { :status => 'Awaiting Marketing Approval' }
      else
        @concept.attributes = { :status => "Pending Approval" }
      end
    end
    

    【讨论】:

      【解决方案5】:

      您可能认为咨询的部门列表是一个足够固定的概念,可以证明名为 Consulted_marketing 等变量的合理性。但是对于增长和干燥(在某种程度上),我更喜欢部门列表。

      你真正拥有的是工作流或状态机,我认为带有转换的部门列表会产生最干净、最可增长的代码。

      代码数据!为您的数据建模,代码将很简单。

      【讨论】:

      • 想法不错的院长。我去看看。
      【解决方案6】:

      在我看来,这像是一条商业规则。因此,您可能希望将其包装成一个函数:

      string GetConceptStatus(bool consulted_legal, bool consulted_marketing)
      { 
          if (consulted_legal && consulted_marketing) {
              return "Pending Approval";
          }
          if (consulted_legal && !consulted_marketing) {
              return "Awaiting Marketing Approval";
          }
          if (!consulted_legal && consulted_marketing) {
              return "Awaiting Legal Approval";
          }
          if (!consulted_legal && !consulted_marketing) {
              return "Awaiting Marketing & Legal Approval";
          }
      }
      

      这还将bools 如何在您的接口中编码的细节与业务规则的实际实现分开。

      但代码的实际结构对我来说看起来不错,因为它可能更好地模拟业务规则。

      【讨论】:

      • 这些类型的 getter 和 setter 是我相信 Rails 中任何 ActiveRecord::Base 类的内置方法。但是感谢您花时间写下您的答案。
      • 这并没有让它们更漂亮;-)
      猜你喜欢
      • 2011-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-03
      • 1970-01-01
      • 1970-01-01
      • 2015-11-20
      • 2017-08-08
      相关资源
      最近更新 更多