Доступ к защищенным методам в Ruby

Я пытаюсь отработать наши для себя модификаторы доступа в Ruby. Я имею:

class Person
  def initialize (first_name, last_name, age)
        @first_name=first_name
        @last_name=last_name
        @age=age
    end


    def show()
        puts @first_name
        puts @last_name
        puts @age
    end

protected
  def compare(other)
    self.instance_variable_get(:@age)<=>other.instance_variable_get(:@age)
  end

end

p1=Person.new("Some", "Body", "99")
p1.show
puts "\n"

p2=Person.new("Who", "Ever", "21")
p2.show
puts "\n"

p1.compare(p2)

Я получаю сообщение об ошибке "Защищенный метод` Compare'вызван для # (NoMethodError)"Я попытался вызвать изнутри класса и без него. Я вставил без версии здесь. Я думал, что защищенные методы можно вызывать для других объектов того же класса. Что означает эта ошибка, и как мне правильно использовать защищенный метод здесь? Спасибо за помощь.

2 ответа

Вы неправильно поняли protected видимость. Рубиновый доктор говорит:

Вторая видимость защищена. При вызове защищенного метода отправитель должен быть подклассом получателя, или получатель должен быть подклассом отправителя. В противном случае будет сгенерировано NoMethodError.

Таким образом, ограничение видимости применяется к отправителю, а не к получателю, как вы думали.

Если вы хотите позвонить compare вне методов экземпляра вам нужно использовать публичную видимость. Вы должны удалить protected модификатор, если вы можете. Это рекомендуемый способ.

Если код исправлен и вы не можете изменить этот фрагмент кода, вы можете использовать Object#send метод. Object#send обойдет ограничение видимости и сможет получить доступ даже к закрытым методам.

p1.send(:compare, p2)

Или вы можете снова открыть класс и изменить видимость compare учебный класс:

# you code here

# reopen and modify visibility
class Person
  public :compare
end

p1.compare(p2)

Вы можете вызвать защищенный метод в публичном методе класса...

class Person
  def initialize (first_name, last_name, age)
    @first_name=first_name
    @last_name=last_name
    @age=age
  end

  def same_age?(other)
    age == other.age
  end

  def show
    puts @first_name
    puts @last_name
    puts @age
  end

  protected

  def age
    @age
  end

end

p1=Person.new("Some", "Body", "99")
p1.show
puts "\n"

p2=Person.new("Who", "Ever", "21")
p2.show
puts "\n"

# calls a method that calls a protected method
p1.same_age?(p2)
=> false

# but you can't call #age directly...
begin 
 p1.age
rescue NoMethodError
  puts "no method error (protected)"
end
Другие вопросы по тегам