参考书籍《Ruby 元编程(第二版)》
Ruby 版本:书上使用的是 2.x,自己使用的 3.1.2

# 关于 self 和私有方法调用之间的问题

# Ruby2.7 之前版本

Ruby 2.7 之前,可以使用 self 调用一个私有写入 / 赋值方法但是使用 self 调用任何其他私有方法将抛出 NoMethodError

示例如下:

ruby2.7以前
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 调用任何私有方法,就像其他语言那样。

ruby2.7及以后
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"
更新于 阅读次数