在 Ruby 中使用 2 个助手构建函数的惯用方法
Posted
技术标签:
【中文标题】在 Ruby 中使用 2 个助手构建函数的惯用方法【英文标题】:Idiomatic ways to structure a function with 2 helpers in Ruby 【发布时间】:2021-09-24 06:00:10 【问题描述】:我编写了一个解决自恋数字 kata on codewars 的方法。
在编写了一个函数后,我提取了两个辅助函数以将我的行数保持在最多 5 行 (Sandi Metz' Rules For Developers)。
这产生了 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 中惯用地完成此操作。
一般来说,我有两个要求:
-
代码应该以某种方式命名空间(考虑到实际项目)。
应该清楚
narcissistic?
是公共API 函数 - 处于更高级别,而其他两个函数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 项目中?如果您知道最佳实践解决方案,请告诉我! :)
任何其他指针也将受到高度赞赏。
【问题讨论】:
【参考方案1】:在我看来,这取决于您的方法的目的,考虑narcissistic
方法的两个名称:
narcissistic?(number)
: 这让我觉得有一个外部类负责检查输入的数字是否自恋。
narcissistic?
: 这让我想到班级本身能够检查它是否自恋。
所以在情况 1 中,假设您有一个包含模块 MathUtils::NarcissisticNumbers
的类 Code
,如果该模块不支持类方法,那么只有类代码 can_do
的实例检查 narcissistic
,然后方法名称应属于上述情况2。
另一方面,如果模块支持类方法,那么方法名应该属于情况1,但是,假设你有一个类Money
需要检查narcissistic
它的值,如果你使用@987654330 @这会让其他人感到困惑(至少他们需要知道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) %>
【讨论】:
【参考方案2】:我同意 Lam 已经写的大部分内容。但是,我会先提取一个您在模块中使用的类。类使处理数据变得更加容易(并遵循建议,您的方法应该最大为 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
【讨论】:
以上是关于在 Ruby 中使用 2 个助手构建函数的惯用方法的主要内容,如果未能解决你的问题,请参考以下文章