如何避免保护块导致的圈复杂度?
How to avoid cyclometic complexity caused by guard blocks?
我的 类
之一中有以下功能
def intraday_time_series(opts)
### Guard block ###
valid_resource = opts[:resource] && [:calories, :steps, :distance, :floors, :elevation].include?(opts[:resource])
valid_date = opts[:date]
valid_detail_level = opts[:detailLevel] && %w(1min 15min).include?(opts[:detailLevel])
raise FitgemOauth2::InvalidArgumentError,
'Must specify resource to fetch intraday time series data for.'\
' One of (:calories, :steps, :distance, :floors, or :elevation) is required.' unless valid_resource
raise FitgemOauth2::InvalidArgumentError, 'Must specify the date to fetch intraday time series data for.' unless valid_date
raise FitgemOauth2::InvalidArgumentError,
'Must specify the data resolution to fetch intraday time series data for.'\
' One of (\"1d\" or \"15min\") is required.' unless valid_detail_level
### actual logic ###
resource = opts.delete(:resource)
date = format_date(opts.delete(:date))
detail_level = opts.delete(:detailLevel)
time_window_specified = opts[:startTime] || opts[:endTime]
resource_path = "user/#{@user_id}/activities/"
if time_window_specified
start_time = format_time(opts.delete(:startTime))
end_time = format_time(opts.delete(:endTime))
resource_path += "#{resource}/date/#{date}/1d/#{detail_level}/time/#{start_time}/#{end_time}.json"
else
resource_path += "#{resource}/date/#{date}/1d/#{detail_level}.json"
end
get_call(resource_path)
end
该函数有一个保护块,用于检查参数(保护块)是否有任何问题,如果没有发现错误,它会处理这些参数。
现在,当我使用 rubocop
分析我的代码时,由于保护块,它报告了很高的循环复杂度。我可以降低复杂性的一种方法是定义另一个函数 guard_for_intraday_time_series
并将所有保护块移到那里。我不认为它是一个合适的解决方案,因为它会为我的项目中的每个函数填充一个保护块。
降低这种复杂性的合适方法是什么,或者它只是不可避免?
你有很多中间局部变量,使代码变得复杂。
我会这样重构它:
def intraday_time_series(resource: nil, date: nil,
detailLevel: nil, startTime: nil, endTime: nil)
unless %i[calories steps distance floors elevation].include?(resource)
raise FitgemOauth2::InvalidArgumentError,
"Must specify resource to fetch intraday time series data for."\
" One of (:calories, :steps, :distance, :floors, or :elevation) is required."
end
unless date
raise FitgemOauth2::InvalidArgumentError,
"Must specify the date to fetch intraday time series data for."
end
unless %w(1min 15min).include?(detailLevel)
raise FitgemOauth2::InvalidArgumentError,
"Must specify the data resolution to fetch intraday time series data for."\
" One of (\"1d\" or \"15min\") is required."
end
resource_path = [
"user", @user_id,
"activities", resource,
"date", format_date(date),
"1d", detailLevel
].join("/")
if startTime || endTime
resource_path =
[resource_path, "time", format_time(startTime), format_time(endTime)].join("/")
end
get_call("#{resource_path}.json")
end
我的 类
之一中有以下功能def intraday_time_series(opts)
### Guard block ###
valid_resource = opts[:resource] && [:calories, :steps, :distance, :floors, :elevation].include?(opts[:resource])
valid_date = opts[:date]
valid_detail_level = opts[:detailLevel] && %w(1min 15min).include?(opts[:detailLevel])
raise FitgemOauth2::InvalidArgumentError,
'Must specify resource to fetch intraday time series data for.'\
' One of (:calories, :steps, :distance, :floors, or :elevation) is required.' unless valid_resource
raise FitgemOauth2::InvalidArgumentError, 'Must specify the date to fetch intraday time series data for.' unless valid_date
raise FitgemOauth2::InvalidArgumentError,
'Must specify the data resolution to fetch intraday time series data for.'\
' One of (\"1d\" or \"15min\") is required.' unless valid_detail_level
### actual logic ###
resource = opts.delete(:resource)
date = format_date(opts.delete(:date))
detail_level = opts.delete(:detailLevel)
time_window_specified = opts[:startTime] || opts[:endTime]
resource_path = "user/#{@user_id}/activities/"
if time_window_specified
start_time = format_time(opts.delete(:startTime))
end_time = format_time(opts.delete(:endTime))
resource_path += "#{resource}/date/#{date}/1d/#{detail_level}/time/#{start_time}/#{end_time}.json"
else
resource_path += "#{resource}/date/#{date}/1d/#{detail_level}.json"
end
get_call(resource_path)
end
该函数有一个保护块,用于检查参数(保护块)是否有任何问题,如果没有发现错误,它会处理这些参数。
现在,当我使用 rubocop
分析我的代码时,由于保护块,它报告了很高的循环复杂度。我可以降低复杂性的一种方法是定义另一个函数 guard_for_intraday_time_series
并将所有保护块移到那里。我不认为它是一个合适的解决方案,因为它会为我的项目中的每个函数填充一个保护块。
降低这种复杂性的合适方法是什么,或者它只是不可避免?
你有很多中间局部变量,使代码变得复杂。
我会这样重构它:
def intraday_time_series(resource: nil, date: nil,
detailLevel: nil, startTime: nil, endTime: nil)
unless %i[calories steps distance floors elevation].include?(resource)
raise FitgemOauth2::InvalidArgumentError,
"Must specify resource to fetch intraday time series data for."\
" One of (:calories, :steps, :distance, :floors, or :elevation) is required."
end
unless date
raise FitgemOauth2::InvalidArgumentError,
"Must specify the date to fetch intraday time series data for."
end
unless %w(1min 15min).include?(detailLevel)
raise FitgemOauth2::InvalidArgumentError,
"Must specify the data resolution to fetch intraday time series data for."\
" One of (\"1d\" or \"15min\") is required."
end
resource_path = [
"user", @user_id,
"activities", resource,
"date", format_date(date),
"1d", detailLevel
].join("/")
if startTime || endTime
resource_path =
[resource_path, "time", format_time(startTime), format_time(endTime)].join("/")
end
get_call("#{resource_path}.json")
end