Подтвердить что ты не робот

Есть ли веские причины для 'private' работать так, как это делается в Ruby?

Мне потребовалось некоторое время, чтобы понять, как работают частные методы в Ruby, и мне действительно кажется очень неудобным. Кто-нибудь знает, есть ли веские причины для того, чтобы частные методы обрабатывались так, как они есть? Это просто исторические причины? Или причины внедрения? Или существуют хорошие логические причины (т.е. Семантические)?

Например:

class Person
  private
  attr_reader :weight
end

class Spy < Person
 private
  attr_accessor :code
 public
  def test
    code          #(1) OK: you can call a private method in self
    Spy.new.code  #(2) ERROR: cannot call a private method on any other object
    self.code     #(3) ERROR!!! cannot call a private method explicitly on 'self'
    code="xyz"    #(4) Ok, it runs, but it actually creates a local variable!!!
    self.code="z" #(5) OK! This is the only case where explicit 'self' is ok
    weight        #(6) OK! You can call a private method defined in a base class
  end
end
  • Поведение Ruby на строках (1), (2) и (5) представляется разумным.
  • Тот факт, что (6) в порядке, немного странный, особенно из Java и С++. Хорошая причина для этого?
  • Я действительно не понимаю, почему (3) терпит неудачу! Объяснение, кто-нибудь?
  • Проблема в строке (4) выглядит как двусмысленность в грамматике, которая не имеет ничего общего с 'private'.

Любые идеи?

4b9b3361

Ответ 1

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

Ruby private аналогичен защищенной Java. Нет эквивалента Ruby для Java. EDIT:. Это решение теперь предоставляет метод фальсификации Java-идеала частного в Ruby-объектах.

Частный определяется как методы/переменные, которые можно называть неявно. Вот почему утверждения 2 и 3 терпят неудачу. Другими словами, частные лимиты используют методы/переменные для контекста класса или подкласса, в котором они определены. Наследование передает частные методы подклассам и поэтому может быть доступно с неявным я. (Объясняет, почему работает инструкция 6.)

Я думаю, что вы ищете что-то более близкое к защищенному. Который ведет себя аналогично Java-аксессуарам, которым не предоставляется видимость (например, public, private, protected) Изменив личное в Spy, чтобы защитить все 6 ваших заявлений. Защищенные методы могут быть вызваны любым экземпляром определяющего класса или их подклассов. Явным образом или неявным образом называемые self являются действительными операторами для защищенных методов, если вызывающий объект является либо классом объекта, который отвечает на вызов, либо наследуется от него.

class Person
  private
  attr_reader :weight
end

class Spy < Person
 protected
  attr_accessor :code
 public
  def test
    code          #(1) OK: you can call a private method in self
    Spy.new.code  #(2) OK: Calling protected method on another instance from same class family or a descendant.
    self.code     #(3) OK: Calling protected method on with explicit self is allowed with protected
    code="xyz"    #(4) Ok, it runs, but it actually creates a local variable!!!
    self.code="z" #(5) OK! This is the only case where explicit 'self' is ok
    weight        #(6) OK! You can call a private method defined in a base class
  end
end

s = Spy.new
s.test # succeeds
s.code #(7) Error: Calling protected method outside of the class or its descendants.

Как для утверждения 4. Вы правы, считая это, чтобы избежать двусмысленности. Это скорее защищает потенциальный вред рубинового динамического характера. Это гарантирует, что вы не сможете переопределить аксессуры, снова открыв класс позже. Ситуация, которая может возникнуть, например, путем eval'ing испорченного кода.

Я могу только догадываться о его дизайнерских решениях, которые привели к этим поведением. По большей части я чувствую, что это сводится к динамичному характеру языка.

P.S. Если вы действительно хотите дать вещи, определение java частного. Доступно только классу, в котором он определен, даже не подклассам. Вы можете добавить метод self.inherited в свои классы, чтобы удалить ссылки на методы, которым вы хотите ограничить доступ.

Сделать атрибут веса недоступным из подклассов:

class Person
  private
  attr_reader :weight

  def initialize
    @weight = 5
  end

  def self.inherited(subclass)
    subclass.send :undef_method, :weight
  end
end

class Spy < Person
 private
  attr_accessor :code
 public
  def test
     weight       
  end
end

Person.new.send(:weight)  # => 5
Spy.new.send(:weight)  #=> Unhelpful undefined method error

Может возникнуть смысл заменить вызов undef_method на что-то вроде этого:

  def self.inherited(subclass)
    subclass.class_eval %{
      def weight 
        raise "Private method called from subclass. Access Denied"
      end
     }
  end

Это обеспечивает гораздо более эффективную ошибку и ту же функциональность.

Отправка необходима для вызова частных методов для других классов. Используется только для доказательства того, что на самом деле все работает.

Который в ретроспективе делает частные и защищенные бесполезными. Если вы действительно серьезно относитесь к защите своих методов, вам придется переопределить отправку, чтобы заблокировать их. Следующий код делает это на основе private_methods объекта:

def send_that_blocks_private_methods(method, *args)
  if private_methods.include?(method.to_s)
    raise "Private method #{method} cannot called be called with send."
  else
    send_that_allows_private_methods(method, *args)
  end
end

alias_method :send_that_allows_private_methods, :send
alias_method :send, :send_that_blocks_private_methods
private :send_that_allows_private_methods

Вы можете указать class_variable private_methods, для которого вы хотите заблокировать доступ, вместо отказа в доступе ко всем приватным методам. Вы также можете сделать send private, но есть законное использование вызова send извне объекта.