代码之家  ›  专栏  ›  技术社区  ›  Nilay Singh

如何简化大条件

  •  1
  • Nilay Singh  · 技术社区  · 6 年前

    我有五个下拉列表,我需要对每个下拉列表设置条件。我的代码是:

       def search(search, compare, year, rain_fall_type)
        if search == 'All'
          if rain_fall_type == 'All'
            all
          else
            if year == 'All'
              if rain_fall_type == "None"
                where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
              else
                # all
                 where(Sector: rain_fall_type).order('id')
              end
            else
              if rain_fall_type == "All"
                order("#{year} ")
    
              elsif rain_fall_type == "None"
                where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
               else
                where(Sector: rain_fall_type).order("#{year} ")
               end
            end
            # where(Year: year).order("#{rain_fall_type} ")
          end
        elsif compare != "None"
          if year == 'All'
            where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
          else
            where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
          end
        else
    
          if rain_fall_type == 'All'
            all.order('id')
          else
            if year == 'All'
    
              if rain_fall_type == "None"
                where('Sector = ? ', search).order('id')
              else
    
                where('Sector = ? ', rain_fall_type).order('id')
              end
            else
    
              if rain_fall_type == "None"
    
                if search == "All"
                  where('Sector = ? ', search).order('id')
                else
                  where('Sector = ? ', search).order('id')
                end
              else
                # all
                where('Sector = ? ', rain_fall_type).order('id')
              end
            end
          end
        end
      end
    

    if else . 我正在尽量减少这种情况。收缩此代码的最佳方法是什么?有人建议我用 switch case 相反。我应该用它吗?如果是,怎么做?

    3 回复  |  直到 6 年前
        1
  •  1
  •   jvillian    6 年前

    首先,你的一些逻辑没有道理:

    def search(search, compare, year, rain_fall_type)
      if search == 'All'
        if rain_fall_type == 'All'
          all
        else
          # rain_fall_type != 'All'
          if year == 'All'
            if rain_fall_type == "None"
              where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
            else
              where(Sector: rain_fall_type).order('id')
            end
          else
            # in rain_fall_type != 'All' branch, so meaningless 'if'
            if rain_fall_type == "All"
              order("#{year} ")
            elsif rain_fall_type == "None"
              where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
            else
              where(Sector: rain_fall_type).order("#{year} ")
            end
          end
        end
      elsif compare != "None"
        # both are same, so meaningless 'if'
        if year == 'All'
          where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
        else
          where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
        end
      else
        # search != 'All'
        if rain_fall_type == 'All'
          all.order('id')
        else
          if year == 'All'
            if rain_fall_type == "None"
              where('Sector = ? ', search).order('id')
            else
              where('Sector = ? ', rain_fall_type).order('id')
            end
          else
            if rain_fall_type == "None"
              # in search != 'All' branch, so meaningless 'if'
              # AND both are same, so again meaningless 'if'
              if search == "All"
                where('Sector = ? ', search).order('id')
              else
                where('Sector = ? ', search).order('id')
              end
            else
              where('Sector = ? ', rain_fall_type).order('id')
            end
          end
        end
      end
    end
    

    还有更多类似的事情,我不会一一指出,因为我们都在扔这些东西 if

    最终,我们将把查询推迟到方法的末尾,如下所示:

    def search(search, compare, year, rain_fall_type)
    
      ...
    
      @query = all 
      @query = @query.where(Sector: @sectors) if @sectors
      @query = @query.order(@order) if @order
      @query
    
    end
    

    这样,你就可以 where order 最后只做一次。这样就省去了大量的打字。请参阅muistooshort的评论了解原因 (Sector: @sectors) 作品。

    @sectors @order . 首先,我将把输入变量赋给实例变量,因为我喜欢这样(并避免变量之间的混淆) @search search ):

    def search(search, compare, year, rain_fall_type)
      @search, @compare, @year, @rain_fall_type = search, compare, year, rain_fall_type
    
      ...
    
      @query = all 
      @query = @query.where(Sector: @sectors) if @sectors
      @query = @query.order(@order) if @order
      @query
    end
    

    现在,这个答案已经持续太久了,所以我不会把你拖到所有血淋淋的细节。但是,添加几个helper方法( sectors_to_use order_to_use )用它们来代替 @部门 ,你基本上会得到这样的结果:

    def search(search, compare, year, rain_fall_type)
      @search, @compare, @year, @rain_fall_type = search, compare, year, rain_fall_type
      @query = all 
      @query = @query.where(Sector: sectors_to_use) if sectors_to_use
      @query = @query.order(order_to_use) if order_to_use
      @query
    end
    
    private 
    
    def sectors_to_use
      return [@rain_fall_type, @compare] if @search != 'All' && @compare != 'None'
      unless @rain_fall_type == 'All'
        if @rain_fall_type == 'None'
          @search == 'All' ? ['Primary', 'Secondary', 'Tertiary'] : [@search]
        else
          [@rain_fall_type]
        end  
      end
    end
    
    def order_to_use
      return nil if (@search == 'All') && (@rain_fall_type == 'All')
      return @year if (@search == 'All') && !(@year == 'All')
      return :id
    end
    

    ifs .

        2
  •  2
  •   Tarek N. Elsamni    6 年前

    你可以使用一个基本上 return something if some_condition? . 这只适用于特定场景(其中一个条件是执行单个语句:

    if condition?
      do_something
    else
      do_something_else
    end
    

    这可以写成:

    return do_something if condition?
    do_something_else
    

    这将减少代码分支。


    另外,另一个建议是调用另一个具有更多条件的方法,而不是在一个快照中嵌套条件。

    坏例子:

    if condition?
      if condition_two?
        do_something_two
      else
        do_something
      end
    else
      do_something_else
    end
    

    这可以写成:

    if condition?
      call_another_method 
    else
      do_something_else
    end
    
    def call_another_method
      if condition_two?
        do_something_two
      else
        do_something
      end
    end
    

    代码中的一个示例可以是:

    if rain_fall_type == 'All'
      all
    else
      if year == 'All'
        if rain_fall_type == "None"
          where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
        else
          # all
            where(Sector: rain_fall_type).order('id')
        end
      else
        if rain_fall_type == "All"
          order("#{year} ")
    
        elsif rain_fall_type == "None"
          where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
          else
          where(Sector: rain_fall_type).order("#{year} ")
          end
      end
    end
    

    return all if rain_fall_type == 'All'
    if year == 'All'
      return where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id') if rain_fall_type == "None"
      where(Sector: rain_fall_type).order('id')
    else
      return order("#{year} ") if rain_fall_type == "All"
      return where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id') if rain_fall_type == "None"
      where(Sector: rain_fall_type).order("#{year} ")
    end
    

    我希望这能有所帮助:)

    注: 这是为了回答 How to simplify big conditions? . 但最初的文章并没有遵循Rails/Ruby的搜索和过滤方式,也没有很好地利用作用域。

        3
  •  2
  •   Verty00    6 年前

    这可能是最好的解释,你应该如何设置这个。

    class Product < ActiveRecord::Base
      # custom_scope_1
      scope :status, -> (status) { where status: status }
      # custom_scope_2
      scope :location, -> (location_id) { where location_id: location_id }
      # custom_scope_3
      scope :search, -> (name) { where("name like ?", "#{name}%")}
    end
    
    def index
       @products = Product.where(nil) # creates an anonymous scope
       @products = @products.status(params[:status]) if params[:status].present?
       @products = @products.location(params[:location]) if params[:location].present?
       @products = @products.search(params[:search]) if params[:search].present?
    end
    

    这可以进一步清理。。。

    def index
      @products = Product.where(nil)
      filtering_params(params).each do |key, value|
        @products = @products.public_send(key, value) if value.present?
      end
    end
    
    private
    
    # A list of the param names that can be used for filtering the Products
    def filtering_params(params)
      params.slice(:status, :location, :search)
    end
    

    此方法使用ruby元编程来循环参数并动态调用预定义的 scopes 在模型上

    您可以将此代码移动到模块中,并将其包含到任何支持筛选的模型中

    app/models/concerns/filterable.rb

    module Filterable
      extend ActiveSupport::Concern
    
      module ClassMethods
        def filter(filtering_params)
          results = self.where(nil)
          filtering_params.each do |key, value|
            results = results.public_send(key, value) if value.present?
          end
          results
        end
      end
    end
    

    app/models/product.rb

    class Product
      include Filterable
      ...
    end
    

    app/controllers/product_controller.rb

    def index
      @products = Product.filter(params.slice(:status, :location, :search))
    end
    

    现在,可以使用控制器中的一行和模型中的一行对模型进行过滤和搜索