解决圈复杂性和感知复杂性
Resolving Cyclomatic and Perceived Complexity
我的测试应用程序中有以下方法:
def on(definition, visit = false, &block)
if @page.is_a?(definition)
block.call @page if block
return @page
end
if @context.is_a?(definition)
block.call @context if block
@page = @context unless @page.is_a?(definition)
return @context
end
@page = definition.new(@browser)
@page.view if visit
@page.correct_url? if @page.respond_to?(:url_matches)
@page.correct_title? if @page.respond_to?(:title_is)
@model = @page
block.call @page if block
@page
end
当我 运行 rubocop 工具针对包含此方法的文件时,我得到以下响应:
C: Cyclomatic complexity for on is too high. [10/6]
C: Perceived complexity for on is too high. [10/7]
我不明白它认为的是什么 "too complex" 因此我无法弄清楚如何解决这个问题。理想情况下,我宁愿不只是告诉 rubocop 避免警告,因为它无疑告诉我一些有用的东西。
至于方法的复杂性,如您所见,我有几个 if
调用,然后我必须使用 @page
object 才能确保它设置正确。 (这个例子,在上下文中,是 Watir-WebDriver object。)
我同意该方法很复杂,因为它需要检查 @page
是否已经存在并设置为某些内容,以及检查 @page
是否应与 [= 相同18=]。但是 - 同样,我不确定该怎么做。
此方法所在模块的完整代码在这里:
https://github.com/jnyman/symbiont/blob/master/lib/symbiont/factory.rb
我最初认为我可以将其分解为不同的方法调用,这可能会降低每个方法的复杂性。但这意味着阅读我的代码的人必须跳到一系列不同的方法才能理解 on
在做什么。在我看来,仅仅移动事物并不能从整体上消除复杂性;相反,它只是在洗牌。还是我错了?
如有任何建议,我们将不胜感激。
更新代码
我已经减少了一些。这是我现在拥有的:
def on(definition, visit = false, &block)
if @page.is_a?(definition)
block.call @page if block
return @page
end
if @context.is_a?(definition)
block.call @context if block
@page = @context
return @context
end
@page = definition.new(@browser)
@page.view if visit
@model = @page
block.call @page if block
@page
end
根据反馈,我删除了一个似乎没有用的 unless
限定符。我还删除了两行,我发现我可以在其他地方更好地使用它们(检查标题和 url)。
这完全删除了 "perceived complexity",只剩下这个:
C: Cyclomatic complexity for on is too high. [7/6]
我似乎 "one point"(或任何术语)太复杂了。
有些地方你的代码是多余的。例如,你有这个:
if @page.is_a?(definition)
...
return ...
end
这意味着,在这之后的任何部分,你都可以假设@page.is_a?(definition)
不是true
,除非你在这部分之后修改@page
。尽管如此,你有:
if @context.is_a?(definition)
... unless @page.is_a?(definition)
end
我的测试应用程序中有以下方法:
def on(definition, visit = false, &block)
if @page.is_a?(definition)
block.call @page if block
return @page
end
if @context.is_a?(definition)
block.call @context if block
@page = @context unless @page.is_a?(definition)
return @context
end
@page = definition.new(@browser)
@page.view if visit
@page.correct_url? if @page.respond_to?(:url_matches)
@page.correct_title? if @page.respond_to?(:title_is)
@model = @page
block.call @page if block
@page
end
当我 运行 rubocop 工具针对包含此方法的文件时,我得到以下响应:
C: Cyclomatic complexity for on is too high. [10/6]
C: Perceived complexity for on is too high. [10/7]
我不明白它认为的是什么 "too complex" 因此我无法弄清楚如何解决这个问题。理想情况下,我宁愿不只是告诉 rubocop 避免警告,因为它无疑告诉我一些有用的东西。
至于方法的复杂性,如您所见,我有几个 if
调用,然后我必须使用 @page
object 才能确保它设置正确。 (这个例子,在上下文中,是 Watir-WebDriver object。)
我同意该方法很复杂,因为它需要检查 @page
是否已经存在并设置为某些内容,以及检查 @page
是否应与 [= 相同18=]。但是 - 同样,我不确定该怎么做。
此方法所在模块的完整代码在这里:
https://github.com/jnyman/symbiont/blob/master/lib/symbiont/factory.rb
我最初认为我可以将其分解为不同的方法调用,这可能会降低每个方法的复杂性。但这意味着阅读我的代码的人必须跳到一系列不同的方法才能理解 on
在做什么。在我看来,仅仅移动事物并不能从整体上消除复杂性;相反,它只是在洗牌。还是我错了?
如有任何建议,我们将不胜感激。
更新代码
我已经减少了一些。这是我现在拥有的:
def on(definition, visit = false, &block)
if @page.is_a?(definition)
block.call @page if block
return @page
end
if @context.is_a?(definition)
block.call @context if block
@page = @context
return @context
end
@page = definition.new(@browser)
@page.view if visit
@model = @page
block.call @page if block
@page
end
根据反馈,我删除了一个似乎没有用的 unless
限定符。我还删除了两行,我发现我可以在其他地方更好地使用它们(检查标题和 url)。
这完全删除了 "perceived complexity",只剩下这个:
C: Cyclomatic complexity for on is too high. [7/6]
我似乎 "one point"(或任何术语)太复杂了。
有些地方你的代码是多余的。例如,你有这个:
if @page.is_a?(definition)
...
return ...
end
这意味着,在这之后的任何部分,你都可以假设@page.is_a?(definition)
不是true
,除非你在这部分之后修改@page
。尽管如此,你有:
if @context.is_a?(definition)
... unless @page.is_a?(definition)
end