5

我正在尝试在这里清理这种极其丑陋的方法,这迫切需要重构,但我不确定哪种结构最能做到这一点(即案例陈述,或者只是一个精心格式化的if then陈述)

乍一看,这似乎是一个放置几个很好when的 's 的 case 语句的理想场所,但我的理解是 case 语句只能用于单个变量,而不是两个,以及各种 irb 的摆弄使用散列或数组来尝试这些语句在这里也没有得到太多启示。

你会怎么做?在检查这样的多个布尔值时,Ruby 中是否有任何常见的技巧来避免这样的代码?

  def has_just_one_kind_of_thing?(item, controller)
    if (controller == 'foos' && item.widgets.blank?) || (controller == 'foos' && item.doohickeys.blank?) || (controller == 'bars' && item.widgets.blank?) || (controller == 'bars' && item.doohickeys.blank?) || (controller == 'bazes' && item.widgets.blank?) || (controller == 'bazes' && item.contraptions.blank?)
      return true
    else 
      return false
    end
  end
4

4 回答 4

13

伙计们 - 似乎这里需要多态性,这无法通过在一种方法中摆弄流控制来解决。

这是我从扩展版本开始的尝试:

def has_just_one_kind_of_thing?(item, controller)
  (controller == 'foos' && item.widgets.blank?)    || 
  (controller == 'foos' && item.doohickeys.blank?) || 
  (controller == 'bars' && item.widgets.blank?)    || 
  (controller == 'bars' && item.doohickeys.blank?) || 
  (controller == 'bazes' && item.widgets.blank?)   || 
  (controller == 'bazes' && item.contraptions.blank?)
end

所以有三种不同的行为 - 每个控制器一个....如果每个控制器足够聪明,可以包含特定于控制器的决策,并且您传入实际的控制器而不仅仅是控制器的名称,您将方法简化为:

def has_just_one_kind_of_thing?(item, controller)
  controller.has_just_one_kind_of_thing?(item)
end

这要求每个控制器针对它所属的控制器类型进行相关的项目处理。所以让我们在 foos、bars 和 bazes 上定义一个方法,称为has_just_one_kind_of_thing?

foos 的示例:

def has_just_one_kind_of_thing?(item)
   item.widgets.blank? || item.doohickeys.blank?
end

贝兹的例子:

def has_just_one_kind_of_thing?(item)
   item.widgets.blank? || item.contraptions.blank?
end

在您想要返回的每个控制器false上,只需应用“常量方法”模式:

def has_just_one_kind_of_thing?(item)
  false
end

这段代码甚至会运行得更快,因为现在,我们不必像控制器类型那样做那么多检查——我们只需对控制器执行一个方法调度。

因此,它在解释型 ruby​​ 中更快-在 jruby或其他可以大量优化方法调度的 ruby​​ 中甚至可能更快...

我们可能可以让它更聪明,但我需要知道这个方法存在于哪个类,也许还有一些关于item.

另一种重构方式是变得更item智能,并且对每种类型的item. 同样,我们需要更多地了解对象模型来判断哪个是最好的......

仍然是第一次削减。

于 2009-11-23T02:26:21.503 回答
10

像这样的东西,也许?

def has_just_one_kind_of_thing?(item, controller)
    return case controller
      when 'foos', 'bars'
        item.widgets.blank? || item.doohickeys.blank?
      when 'bazes'
        item.widgets.blank? || item.contraptions.blank?
      else 
        false
    end
  end

外部返回可能不是必需的(不完全确定 Ruby 需要什么,我自己对它还是很陌生),但我更喜欢把它留在里面,所以意图很明显。

于 2009-11-23T01:27:42.427 回答
4

首先,Ruby 总是默认返回最后一条语句的值,所以我们可以去掉 if/else:

def has_just_one_kind_of_thing?(item, controller)
    (controller == 'foos' && item.widgets.blank?) || (controller == 'foos' && item.doohickeys.blank?) || (controller == 'bars' && item.widgets.blank?) || (controller == 'bars' && item.doohickeys.blank?) || (controller == 'bazes' && item.widgets.blank?) || (controller == 'bazes' && item.contraptions.blank?)
end

现在让我们重新格式化以提高可读性。

def has_just_one_kind_of_thing?(item, controller)
  (controller == 'foos' && item.widgets.blank?)    || 
  (controller == 'foos' && item.doohickeys.blank?) || 
  (controller == 'bars' && item.widgets.blank?)    || 
  (controller == 'bars' && item.doohickeys.blank?) || 
  (controller == 'bazes' && item.widgets.blank?)   || 
  (controller == 'bazes' && item.contraptions.blank?)
end

在那里,现在更容易看到算法中的模式。看起来这里有两个问题:控制器是 [foos|bars|bazes] 之一,并且是小部件或 doohickeys 空白。让我们分解第一个问题:

def has_just_one_kind_of_thing?(item, controller)
  %w[foos bars bazes].include?(controller)  &&
    (item.widgets.blank? || item.doohickeys.blank?)
end

这将方法缩小到可管理的大小。但是我从方法名称推断出您正在寻找小部件或 doohickeys 有项目的情况,但不是两者都有,也不是两者都没有。如果是这样,XOR 可能更合适:

def has_just_one_kind_of_thing?(item, controller)
  %w[foos bars bazes].include?(controller)  &&
    (item.widgets.blank? ^ item.doohickeys.blank?)
end
于 2009-11-23T01:29:56.273 回答
3

德摩根的布尔逻辑定理有两个陈述。

1. (A and B) 等价于 not(notA or notB)

not(A and B) 等价于 (notA or notB)

2. (A or B) 等价于 not(notA and notB)

not(A or B) 等价于 (notA and notB)

..

  1. 如果是三明治(低于 2 美元且是鲑鱼),则购买三明治 = 如果是三明治,则不购买(不少于 2 美元或不是鲑鱼)。

  2. 如果(V 开启或 Avatar 开启)则看电视 = 如果(V 关闭且 Avatar 关闭)则不看电视。

进一步的布尔代数等,

  1. (A 和 B) 或 (A 和 C) = A 和 (B 或 C)

  2. (A 或 B)或 C = A 或 B 或 C


原逻辑:

  (controller == 'foos' && item.widgets.blank?)    || 
  (controller == 'foos' && item.doohickeys.blank?) || 

  (controller == 'bars' && item.widgets.blank?)    || 
  (controller == 'bars' && item.doohickeys.blank?) || 

  (controller == 'bazes' && item.widgets.blank?)   || 
  (controller == 'bazes' && item.contraptions.blank?)


减少
(foos and widgets) or (foos and hickeys) = foos and (widgets or hickeys):

  (
    (controller == 'foos' &&
      (item.widgets.blank? || item.doohickeys.blank?)
    ) || 

    (controller == 'bars' &&
      (item.widgets.blank? || item.doohickeys.blank?)
    ) || 

    (controller == 'bazes' &&
      (item.widgets.blank? || item.contraptions.blank?)
    )
  )


减少
(foos 和 items)或(bars 和 items)=(foos 或 bar)和 items:

  (
    (controller == 'foos' || controller == 'bars') &&
    (item.widgets.blank? || item.doohickeys.blank?)
  ) ||

  (controller == 'bazes' &&
    (item.widgets.blank? || item.contraptions.blank?)
  )


逻辑缩减已从原来的 6 行减少到 3.5 行。尽管这个练习不涉及德摩根的简单布尔代数操作,但德摩根经常适用于其他情况。

你可能会说,为什么我每次写一堆逻辑语句都要费这么大的劲呢?简化后的逻辑与原始逻辑没有任何相似之处,这不是很好的自记录代码。

确切地!简化的逻辑让您以更简单的方式看待它,而原始逻辑与您最初应该拥有的更简单的逻辑没有任何相似之处。

于 2009-11-23T06:19:22.997 回答