参考书籍《Ruby 元编程(第二版)》
Ruby 版本:书上使用的是 2.x,自己使用的 3.1.2
# 关于 self 和私有方法调用之间的问题
# Ruby2.7 之前版本
在 Ruby 2.7 之前,可以使用 self 调用一个私有写入 / 赋值方法,但是使用 self 调用任何其他私有方法将抛出 NoMethodError。
示例如下:
class Counter | |
def reset | |
self.count = 0 # 允许调用 | |
puts self.count # 抛出异常 | |
end | |
private | |
attr_accessor :count | |
end | |
Counter.new.reset |
上面的示例执行 self.count = 0
语句时,不会出现问题,但是执行到 puts self.count
语句时就会抛出 NoMethodError 错误。这种前后不一致的行为让人感到十分困惑。
# Ruby2.7 及以后版本
在 Ruby 2.7 版本,Ruby 修复这种让人感到困惑的不一致行为。换而言之,从 Ruby 2.7 开始,可以使用 self 调用任何私有方法,就像其他语言那样。
class Counter | |
def reset | |
self.count = 0 # 允许调用 | |
puts self.count # 允许调用 | |
end | |
private | |
attr_accessor :count | |
end | |
Counter.new.reset |
# 细化(Refine)
使用打开类的方式扩展代码,有一个很明显的问题:修改时全局性的,很有可能无意中影响其他代码的正常使用。但通过细化的方式可以使修改只作用于局部,很好的解决了之前的问题。
# 细化的定义方式
使用细化,首先需要先定一个模块,然后在这个模块的定义中调用 refine 方法。
module StringExtentions | |
refine String do | |
def something | |
"something..." | |
end | |
end | |
end |
# 通过 using 让细化生效
在上述代码中,为 String 类添加了一个 something 方法,但细化在默认情况下并不生效,需要使用 using 让修改生效。
module StringExtentions | |
refine String do | |
def something | |
"something..." | |
end | |
end | |
end | |
# 在当前文件内生效 | |
using StringExtentions | |
"My Something".something #=> something... |
从 Ruby 2.1 开始,可以在一个模块内调用 using 方法,此时细化的作用范围仅限于当前模块。
module StringExtentions | |
refine String do | |
# 为 String 类修改了 reverse 方法的定义 | |
def reverse | |
"My reverse method..." | |
end | |
end | |
end | |
module StringStuff | |
# 在 StringStuff 模块内生效 | |
using StringExtentions | |
"test".reverse #=> "My reverse method..." | |
end | |
# 出 StringStuff 模块失效 | |
"test".reverse #=> "tset" |