提问人:Peter Slotko 提问时间:7/15/2021 更新时间:7/16/2021 访问量:79
在 Ruby 中使用 2 个助手构建函数的惯用方法
Idiomatic ways to structure a function with 2 helpers in Ruby
问:
我为代码大战上的自恋数字 kata 编写了一个解决方案。
在编写了一个函数后,我提取了两个辅助函数,以便将行数保持在最多 5 行(Sandi Metz 的开发人员规则)。
这导致了 3 个功能:
def digits(number)
number
.to_s
.chars
.map(&:to_i)
end
def checksum(digits, exp)
digits
.map { |d| d**exp }
.reduce(:+)
end
def narcissistic?(number)
digits = digits(number)
exp = digits.length
checksum = checksum(digits, exp)
checksum == number
end
现在,我想假装这段代码应该添加到一个更大的实际项目中。我的问题是,在 Ruby 中应该如何惯用地做到这一点。
一般来说,我有两个要求:
- 代码应该以某种方式命名(考虑到一个真实世界的项目)。
- 应该清楚的是,公共 API 函数 - 处于较高级别,而其他两个函数则处于较低抽象级别。
narcissistic?
digits
checksum
到目前为止,我的推理是:这段代码实际上并不需要 OOP。但是在 Ruby 中,将某些内容放入命名空间的唯一方法是创建一个 或 .Class
Module
也许,一个会是更好的选择?不过,我不确定我是否应该选择:Module
module MathUtils::NarcissisticNumbers
def self.narcissistic?(number)
...
end
private
...
end
与
module MathUtils::NarcissisticNumbers
def narcissistic?(number)
...
end
private
...
end
你会如何把这段代码引入到一个 Ruby 项目中?如果您知道最佳实践解决方案,请告诉我!:)
任何其他指示也将不胜感激。
答:
在我看来,这取决于您的方法的目的,请考虑 2 个方法名称:narcissistic
narcissistic?(number)
:这让我觉得有一个外部班级负责检查输入的数字是否自恋。narcissistic?
:这让我想到了班级本身能够检查它是否自恋。
因此,在情况 1 中,假设您有一个包含该模块的类,如果该模块不支持类方法,则只有类代码检查的实例,那么方法名称应该属于上述情况 2。Code
MathUtils::NarcissisticNumbers
can_do
narcissistic
另一方面,如果模块支持类方法,那么方法名称应该属于情况 1,但是,假设你有一个类需要检查它的值,如果你使用它会让其他人感到困惑(至少他们需要知道是什么),但如果你使用它是完全有意义的,其他人会立即明白这是一种检查数字方法。Money
narcissistic
Code.narcissistic?(money.value)
Code
MathUtils::NarcissisticNumbers.narcissistic?(money.value)
我建议你让是一个并创建另一个模块MathUtils::NarcissisticNumbers
module_function
narcissistic?
module MathUtils::NarcissisticNumbers
module_function
def is_narcissistic?(number)
end
end
module Narcissistic
def narcissistic?
MathUtils::NarcissisticNumbers.is_narcissistic?(self.value)
end
end
class Code
include Narcissistic
end
class Money
include Narcissistic
end
code = Code.new(...)
code.narcissistic?
# for those classes that only check narcissistic? internally
# then you can include MathUtils::NarcissisticNumbers
# since is_narcissistic?(number) become a private method
class FormatNumber
include MathUtils::NarcissisticNumbers
def format(number)
if is_narcissistic?(number)
# ...
else
# ...
end
end
end
# you can use MathUtils::NarcissisticNumbers wherever you want (as helper)
# on other classes that not include Narcissistic, including views , ...
<% if MathUtils::NarcissisticNumbers.is_narcissistic?(input) %>
我同意林已经写过的大多数事情。但是,我会先提取一个您在模块中使用的类。类使处理数据变得更加容易(并且遵循建议,您的方法应最大为 5LOC)。
class MathUtils::NarcissisticNumber
def initialize(number)
@number = number
end
def valid?
checksum == number
end
private
attr_reader :number
def checksum
digits.map { |d| d**exponent }.reduce(:+)
end
def digits
@digits ||= number.to_s.chars.map(&:to_i)
end
def exponent
@exponent ||= digits.length
end
end
通过使用一个类,我们能够删除所有方法参数和临时变量。现在,我们可以在 Liam 建议的帮助程序模块中使用此类。
module MathUtils::NarcissisticNumbers
def narcistic?(number)
NarcissisticNumber.new(number).valid?
end
end
下一个:类内的命名空间方法
评论