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

Существует класс Стека в API. Это удовлетворит Ваши потребности?

25
задан Andrew Grimm 27 March 2012 в 23:33
поделиться

1 ответ

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

Закрытый Ruby аналогичен защищенному Java. Не существует Ruby-эквивалента Java private. РЕДАКТИРОВАТЬ: Это решение теперь предоставляет метод подделки Java-идеала приватности в объектах Ruby.

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

Я думаю, вы ищете что-то более защищенное. Изменив приват в 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. Вы правы, предполагая, что это чтобы избежать двусмысленности. Это скорее защита от потенциального вреда динамической природы рубина. Это гарантирует, что вы не сможете переопределить методы доступа, открыв класс позже. Ситуация, которая может возникнуть, например, при оценке испорченного кода.

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

PS Если вы действительно хотите дать вещам java-определение private. Доступно только тому классу, в котором он определен, даже не подклассам. Вы можете добавить к своим классам метод 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

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

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

Что, оглядываясь назад, делает частные и защищенные бесполезными. Если вы действительно серьезно относитесь к защите своих методов, вам придется переопределить send, чтобы заблокировать их. Следующий код делает это на основе 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 частной, но есть законное использование вызова send извне объекта.

32
ответ дан 28 November 2019 в 21:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: