Ruby元编程(二)
参考书籍《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"