如何重构这组庞大的 if 语句?

Posted

技术标签:

【中文标题】如何重构这组庞大的 if 语句?【英文标题】:How do I refactor this bulky set of if statements? 【发布时间】:2019-05-04 06:22:33 【问题描述】:

我有以下有效的代码,但我希望它更干爽、更优雅。感觉很丑,有很多代码味道。

理想情况下,我不想使用 CASE 语句,因为这也不太像 ruby​​ 风格。

if @property_status.eql? :rent
  if @property_type.eql? :residential
    @results = @search.results.for_rent.residential.order("# @sort_by  # @sort_order ").all.paginate(page: @page, per_page: @per_page)
  elsif @property_type.eql? :commercial
    @results = @search.results.for_rent.commercial.order("# @sort_by  # @sort_order ").all.paginate(page: @page, per_page: @per_page)
  else
    @results = @search.results.for_rent.order("# @sort_by  # @sort_order ").all.paginate(page: @page, per_page: @per_page)
  end
elsif @property_status.eql? :sale
  if @property_type.eql? :residential
    @results = @search.results.for_sale.residential.order("# @sort_by  # @sort_order ").all.paginate(page: @page, per_page: @per_page)
  elsif @property_type.eql? :commercial
    @results = @search.results.for_sale.commercial.order("# @sort_by  # @sort_order ").all.paginate(page: @page, per_page: @per_page)
  else
    @results = @search.results.for_sale.order("# @sort_by  # @sort_order ").all.paginate(page: @page, per_page: @per_page)
  end
else
  @results = @search.results.order("# @sort_by  # @sort_order ").all.paginate(page: @page, per_page: @per_page)
end

想法?

【问题讨论】:

我投票结束这个问题,因为它更适合Code Review。 【参考方案1】:

首先认识到这一点:

@results = @search.results.for_rent.residential.order("# @sort_by  # @sort_order ").all.paginate(page: @page, per_page: @per_page)

相当于:

@results = @search.results
@results = @results.for_rent
@results = @results.residential
@results = @results.order(@sort_by => @sort_order).paginate(page: @page, per_page: @per_page)

假设@sort_by@sort_order 总是设置为当然。重要的是您可以逐个构建查询,并根据您的实例变量选择要添加的部分。您可以添加几个简单的助手:

def add_property_status_to(query)
  case @property_status
  when :rent, :sale
    query.public_send("for_#@property_type")
  else
    query
  end
end

def add_property_type_to(query)
  case @property_type
  when :residential, :commercial
    query.public_send(@property_type)
  else
    query
  end
end

然后这样说:

query = @search.results
query = add_property_status_to(query)
query = add_property_type_to(query)

@results = query.order(@sort_by => @sort_order).paginate(page: @page, per_page: @per_page)

您可以将add_property_status_toadd_property_type_to 方法视为本地单次使用范围。如果您需要在多个地方使用它们,那么您可以将它们作为 @search.results 的类方法,然后说:

query = @search.results
query = query.with_property_status(@property_status)
query = query.with_property_type(@property_type)

@results = query.order(@sort_by => @sort_order).paginate(page: @page, per_page: @per_page)

【讨论】:

【参考方案2】:

我没有测试它,但我认为是这样的:

def results
  return results_for_rent if for_rent?
  return results_for_sale if for_sale?
  default_results
end

def for_rent?
  @property_status.eql? :rent
end

def for_sale?
  @property_status.eql? :sale
end

def default_results
  @results = @search.results.order("# @sort_by  # @sort_order 
   ").all.paginate(page: @page, per_page: @per_page)
end

def results_for_rent
  if [:residential, :commercial].includes?(@property_type)
    results(@property_type, type: :for_rent)
    return 
  end
  @results = @search.results.for_rent.commercial.order("# @sort_by  # 
  @sort_order ").all.paginate(page: @page, per_page: @per_page)
end

def results_for_sale
  if [:residential, :commercial].includes?(@property_type)
    results(kind, type: :for_sale)
    return
  end
  @results = @search.results.for_sale.order("# @sort_by  # @sort_order 
  ").all.paginate(page: @page, per_page: @per_page)
end

def results(kind:, type: )
  @results = @search.results.send(kind).send(type).order("# @sort_by  # 
  @sort_order ").all.paginate(page: @page, per_page: @per_page)
end

您也可以重构相同的 .all.pagination 部分,但您明白了。您还可以通过添加一个参数来重构 results_for_rentresults_for_sale 方法

【讨论】:

谢谢你,虽然这感觉它比原来的更难读和更难理解。所以我觉得我为半干性牺牲了可理解性。 它更容易理解,因为:results 方法只有 3 个条件。 请注意,Rails 添加了Object#in?,因此您可以说@property_type.in?(%i[residential commercial]) 之类的内容,通常比等效的#include? 版本更容易阅读。

以上是关于如何重构这组庞大的 if 语句?的主要内容,如果未能解决你的问题,请参考以下文章

重构许多嵌套 if 或链式 if 语句

为了重构庞大的代码库,我应该记住啥?

重构此函数以在 if 语句 Javascript 内部和外部一致地使用“return”

Java重构-策略模式状态模式卫语句

重构双foreach和if语句到java 8解决方案

译重构:这个类太庞大了