代码之家  ›  专栏  ›  技术社区  ›  MiniQuark

“private”是否有充分的理由像Ruby中那样工作?

  •  25
  • MiniQuark  · 技术社区  · 16 年前

    我花了一段时间才明白怎么做 私有的

    例如:

    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和C++。有什么好的理由吗?
    • 我真的不明白为什么(3)失败了!有人解释吗?
    • 第(4)行的问题看起来像语法中的歧义,与“private”无关。

    1 回复  |  直到 14 年前
        1
  •  34
  •   EmFi    16 年前

    您可能会发现阅读ruby对 public, private and protected. (跳到访问控制)

    编辑: 这个解决方案现在提供了一种在Ruby对象中假装Java的私有理想的方法。

    Private定义为可以 只有

    我想你在找更接近保护的东西。其行为类似于未提供可见性的Java访问器(例如:public、private、protected) 通过将Spy中的private更改为protected,可以保护您所有的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。你认为这是为了避免模棱两可是正确的。它更能保护ruby动态特性的潜在危害。它确保您不能通过稍后再次打开类来覆盖访问器。可能出现的情况,例如通过评估受污染的代码。

    我只能推测导致这些行为的设计决策。我觉得这主要是因为语言的动态性。

    另外,如果你真的想给东西一个私有的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_方法调用替换为以下内容可能更有意义:

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

    这提供了一个更有用的错误和相同的功能。

    send对于避免调用其他类的私有方法是必要的。只是用来证明事情确实在起作用。

    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
    

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