从Ruby中的实例方法调用受保护的类方法

时间:2012-06-21 15:28:05

标签: ruby-on-rails ruby visibility

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

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

    protected
    def self.class_bang
      puts "bang"
    end
end

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

更新:

根据Sergio Tulentsev的回复(thx进行更正),我将更新我对一个代码片段的关注,该代码片段总结了我对方法可见性的关注,同时仍然在已经定义的范围内。

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(这次我检查了它:))。

3 个答案:

答案 0 :(得分:4)

编辑:回答更新的问题

禁止使用显式接收器调用私有方法。您必须使用隐式接收器(private_bang,不使用self)或使用send。有关详细信息,请参阅my another answer

顺便说一句,最初的问题是从实例方法调用类实例方法。您的澄清不包括此。但如果仍然如此,则必须使用self.class.send或将方法设为公共(以便您可以使用显式接收器)。

答案 1 :(得分:1)

让我们考虑一个私有类方法(因为受保护的类方法don't make sense)。

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

让我们看看如何在不使用send的情况下执行此操作。

privateprotected宏仅影响当前范围的实例方法,而不影响类方法。以下是重写原始代码的三种方法:

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

答案 2 :(得分:0)

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

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