"私人"是否有充分理由按照Ruby的方式工作?

Min*_*ark 25 ruby language-design private access-specifier

我花了一段时间才了解私有方法在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
Run Code Online (Sandbox Code Playgroud)
  • Ruby在第(1),(2)和(5)行上的行为似乎是合理的.
  • (6)没问题的事实有点奇怪,特别是来自Java和C++.有什么好理由吗?
  • 我真的不明白为什么(3)失败了!一个解释,有人吗?
  • 第(4)行的问题看起来像语法中的含糊不清,与"私人"无关.

有任何想法吗?

EmF*_*mFi 32

您可能会发现阅读ruby对public,private和protected的定义很有帮助.(跳到访问控制)

Ruby的私有类似于Java的受保护.没有Ruby相当于Java的私有.编辑:这个解决方案现在提供了一种方法来伪造Java在Ruby对象中的私有理想.

Private被定义为只能隐式调用的方法/变量.这就是语句2和3失败的原因.换句话说,private将方法/变量限制为定义它们的类或子类的上下文.继承将私有方法传递给子类,因此可以使用隐式self访问.(解释为什么声明6有效.)

我认为你正在寻找更接近受保护的东西.其行为类似于未获得可见性的Java访问器(例如:public,private,protected)通过将Spy中的private更改为受保护的所有6个语句都可以工作.受保护的方法可以由定义类的任何实例或其子类调用.只要调用者是响应调用的对象的类,或者从调用继承,对于受保护的方法显式或隐式调用都是有效的语句.

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.
Run Code Online (Sandbox Code Playgroud)

至于陈述4.你认为这是为了避免含糊不清是正确的.它更能保护红宝石动态性的潜在危害.它确保您不能通过稍后再次打开该类来覆盖访问者.可能出现的情况,例如通过评估受污染的代码.

我只能推测他导致这些行为的设计决策.对于大多数人来说,我觉得这取决于语言的动态性.

PS如果你真的想给私人的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
Run Code Online (Sandbox Code Playgroud)

将undef_method调用替换为以下内容可能更有意义:

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

这提供了更有用的错误和相同的功能.

发送是必要的,以便为其他类调用私有方法.仅用于证明事情确实有效.

事后看来,使私人和受保护无用.如果您真的非常认真地保护自己的方法,则必须覆盖发送以阻止它们.以下代码基于对象的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
Run Code Online (Sandbox Code Playgroud)

您可以指定要阻止访问的private_methods的class_variable,而不是拒绝访问所有私有方法.您也可以将send设为私有,但是从对象外部调用send是合法的用法.