处理许多条件的更好方法?

时间:2015-07-27 21:02:38

标签: ruby design-patterns

我们有许多不同类型的Feed。一个Feed有很多feed_comments。根据Feed类型,我想返回一个特定的字符串。

if feed.type == 1
  if nested_comment
    str = "test"
  else if comment
    str = "test1"
  else
    str = "test2"
  end 
else if feed.type == 2
  if nested_comment
    str = "test3"
  else if comment
    str = "test4"
  else
    str = "test5"
  end 
else if feed.type == 3
  if nested_comment
    str = "test6"
  else if comment
    str = "test7"
  else
    str = "test8"
  end 
else if feed.type == 4
  if nested_comment
    str = "test9"
  else if comment
    str = "test10"
  else
    str = "test11"
  end 
end

如果我们有更多条件,编写上述代码的更好方法是什么,以便将来不必更改现有代码?

3 个答案:

答案 0 :(得分:4)

要获得更易于管理和可读的代码,您可以尝试使用case statements。除此之外,我无法看到你如何压缩代码,因为每种可能性都有一个明显的结果,就像有人指出的那样。

case feed.type
when 1
  case
  when nested_comment then str = "test"
  when comment        then str = "test2"
  else                     str = "test3"
  end
when 2
  case
  when nested_comment then str = "test"
  when comment        then str = "test2"
  else                     str = "test3"
  end
 # repeat for each feed number type
end

答案 1 :(得分:3)

此代码可以从Replace Conditional with Polymorphism Refactoring

中受益

通常,在面向对象的语言中,永远根本不需要任何条件,因为面向对象的语言具有运行时多态消息调度,这比任何条件都强大。您可以始终用多态替换条件; Smalltalk就是存在的证据,它甚至没有语言中的条件,它们是使用消息发送在库中实现的,有点像这样:

class TrueClass
  def if_then_else(then_part, else_part)
    then_part.()
  end
end

class FalseClass
  def if_then_else(then_part, else_part)
    else_part.()
  end
end

(2 < 3).if_then_else(-> { puts 'True' }, -> { puts 'False' })
# True

Ruby 确实有条件,但你不需要它们。

那么,运行时多态消息调度究竟做了什么?好吧,它基本上根据类型执行不同的代码。例如,如果你说

foo.bar

将根据bar的类型运行不同的foo方法。

在您的情况下,您根据决定执行哪个代码而使用的值字面上称为type,因此,您实际上只是重新实现了Ruby的基本特性:基于类型执行不同的代码只是消息调度,无论如何Ruby都是自己做的。

因此,在您的情况下,您将拥有4个不同的Feed类和2个不同的Comment类。

现在,在您的情况下,它有点复杂,因为结果不仅取决于Feed类型,还取决于评论类型。 Ruby没有多个调度,所以,我们要么需要为feed类型和注释类型的每个组合引入新类,要么在最终代码中使用一些条件。

因此,让我们开始慢慢改进您的代码。首先,我相信您的代码中的elsif代替else if

if feed.type == 1
  if nested_comment
    str = "test"
  elsif comment
    str = "test1"
  else
    str = "test2"
  end 
elsif feed.type == 2
  if nested_comment
    str = "test3"
  elsif comment
    str = "test4"
  else
    str = "test5"
  end 
elsif feed.type == 3
  if nested_comment
    str = "test6"
  elsif comment
    str = "test7"
  else
    str = "test8"
  end 
elsif feed.type == 4
  if nested_comment
    str = "test9"
  elsif comment
    str = "test10"
  else
    str = "test11"
  end 
end

其次,我们可以利用Ruby中的条件是表达式而不是语句的事实(事实上,Ruby中的所有内容都是表达式,没有语句),因此评估为一个值:

str = if feed.type == 1
  if nested_comment
    "test"
  elsif comment
    "test1"
  else
    "test2"
  end 
elsif feed.type == 2
  if nested_comment
    "test3"
  elsif comment
    "test4"
  else
    "test5"
  end 
elsif feed.type == 3
  if nested_comment
    "test6"
  elsif comment
    "test7"
  else
    "test8"
  end 
elsif feed.type == 4
  if nested_comment
    "test9"
  elsif comment
    "test10"
  else
    "test11"
  end 
end

现在,我们将if替换为case个表达式:

str = case feed.type
when 1
  case
  when nested_comment
    "test"
  when comment
    "test1"
  else
    "test2"
  end 
when 2
  case
  when nested_comment
    "test3"
  when comment
    "test4"
  else
    "test5"
  end 
when 3
  case
  when nested_comment
    "test6"
  when comment
    "test7"
  else
    "test8"
  end 
when 4
  case
  when nested_comment
    "test9"
  when comment
    "test10"
  else
    "test11"
  end 
end

现在,让我们重新格式化一下,以便更轻松地了解正在发生的事情:

str = case feed.type
when 1
  case
  when nested_comment then "test"
  when comment        then "test1"
  else                     "test2"
  end 
when 2
  case
  when nested_comment then "test3"
  when comment        then "test4"
  else                     "test5"
  end 
when 3
  case
  when nested_comment then "test6"
  when comment        then "test7"
  else                     "test8"
  end 
when 4
  case
  when nested_comment then "test9"
  when comment        then "test10"
  else                     "test11"
  end 
end

我们重构的时间:

class Feed1
  def magic_string
    case
    when nested_comment then "test"
    when comment        then "test1"
    else                     "test2"
    end
  end
end

class Feed2
  def magic_string
    case
    when nested_comment then "test3"
    when comment        then "test4"
    else                     "test5"
    end
  end
end

class Feed3
  def magic_string
    case
    when nested_comment then "test6"
    when comment        then "test7"
    else                     "test8"
    end
  end
end

class Feed4
  def magic_string
    case
    when nested_comment then "test9"
    when comment        then "test10"
    else                     "test11"
    end
  end 
end

str = feed.magic_string

我们可以通过引入一个封装注释检查的方法来进一步减少一些重复(或者,就像我说的,我们可以引入注释类)。

class Feed
  def comment_string(nested_comment_string, comment_string, other_string)
    case
    when nested_comment then nested_comment_string
    when comment        then comment_string
    else                     other_string
    end
  end
end

class Feed1 < Feed
  def magic_string
    comment_string("test", "test1", "test2")
  end
end

class Feed2 < Feed
  def magic_string
    comment_string("test3", "test4", "test5")
  end
end

class Feed3 < Feed
  def magic_string
    comment_string("test6", "test7", "test8")
  end
end

class Feed4 < Feed
  def magic_string
    comment_string("test9", "test10", "test11")
  end 
end

str = feed.magic_string

答案 2 :(得分:1)

results = {
  1 => {
    nested_comment: 'test1',
    comment: 'test2',
    else: 'test3'
  }
}

comment_type = nested_comment ? :nested_comment : comment ? :comment : :else
results[feed.type][comment_type]