0

我一直有这个令人烦恼的反复出现的主题;比方说,我有一个类,它定义了一个实例方法和一个受保护的类方法。实例方法必须调用类方法。为了做到这一点,我不得不打破可见性规则并使用危险的“发送”功能。像这样的东西:

class Bang
    def instance_bang
      self.class.send(:class_band)
    end

    protected
    def self.class_bang
      puts "bang"
    end
end

我觉得这很糟糕,因为类方法应该在类范围内使用,因此应该在其中保持可见和可调用,对吗?是否有另一种方法可以在实例方法中使用类方法,需要依赖“发送”函数,因此不会破坏可见性?

更新:

在 Sergio Tulentsev 的回复之后(感谢更正),我将使用代码片段更新我的担忧,该代码片段总结了我对仍在定义范围内时考虑方法可见性的担忧。

class Bang
  def instance_bang
    private_bang = 1
    self.private_bang(private_bang)
  end
  private
  def private_bang(p)
    puts "bang"
    p
  end
end

调用 Bang.new.instance_bang 将引发异常,除非您在该 private_bang 调用上使用 send(这次我检查了它:))。

4

3 回答 3

4

编辑:回答更新的问题

禁止使用显式接收者调用私有方法。您要么必须使用隐式接收器(private_bang,无self)或使用send. 请参阅我的另一个答案以获取更多信息。

顺便说一句,最初的问题是关于从实例方法调用类实例方法。您的澄清不包括这一点。但如果这仍然是真的,您必须使用self.class.send或公开该方法(以便您可以使用显式接收器)。

于 2012-06-21T17:48:49.737 回答
2

让我们考虑一个私有类方法(因为受保护的类方法没有意义)。

我们知道一个实例可以调用自己的私有方法,只要它不使用显式接收器(self.call_something_private)。您似乎还希望实例可以在其自己的类上调用私有类方法,但事实并非如此。

让我们看看一种不使用send.

privateandprotected宏只影响当前作用域的实例方法,不影响类方法。以下是重写原始代码的三种方法:

class Bang
  def instance_bang
    self.class.class_bang
  end

  # declare method visibility after
  def self.class_bang
    puts "bang"
  end
  private_class_method :class_bang

  # inline
  private_class_method def self.class_bang
    puts "bang"
  end

  # class scope
  class << self
    # the private macro works here because we're inside the class scope
    private

    def class_bang
      puts "bang"
    end
  end
end

所以现在我们要在类上公开一个接口来调用class_bang,但前提是它被 的实例调用Bang

class Bang
  def instance_bang
    self.class.invoke_class_bang(self)
  end

  class << self
    private

    def class_bang
      puts "bang"
    end

    public

    # we ask the receiver to pass itself as an argument ...
    def invoke_class_bang(receiver)
      # ... so that we can check whether it's
      class_bang if receiver.is_a?(Bang)
    end
  end
end

不过,这不是一个很好看的解决方案。这是一个更狡猾的方法:

class Bang
  def initialize
    def self.instance_bang() self.class.method(:class_bang).call end
  end

  class << self
    private
    def class_bang
      puts "bang"
    end
  end
end
于 2016-04-07T18:57:52.463 回答
0

“类方法应该在类范围内使用,因此应该在其中保持可见和可调用,对吧?” 是的,这是正确的,这就是 Ruby 表现出来的行为。(作为澄清点,实例范围不在“类范围内”。它们适当地是分开的。)

send解决方案是子类化或重新打开类以添加公共类方法来访问受保护的类方法。

于 2012-06-21T15:40:55.030 回答