ruby中的Private vs Protected方法

时间:2016-06-21 18:57:17

标签: ruby-on-rails ruby access-specifier

如果某个方法受到保护,则可以调用该方法 由定义类或其子类的任何实例。如果方法是私有的,它可以 只能在调用对象的上下文中调用 - 它永远不可能访问 直接使用另一个对象的私有方法,即使该对象与该对象属于同一个类 呼叫者。

这个定义是我在搜索ruby中私有和受保护方法之间的区别时从网上获得的。

我对此有两个疑问

class Abc
  def abc
    xyz
  end
 protected 
  def xyz
    p "hai"
  end
end

a=Abc.new
a.abc

在这个我隐含地在对象a上调用xyz,这就是我将如何调用xyz,即使它是私有的。所以“它可能被定义类的任何实例调用”这是什么意思?????

其次,

 class Abc
   def abc(obj)
     obj.xyz1
     obj.xyz2
     obj.xyz3
   end
 end

 class Xyz
   def xyz1
     p "called"
   end

  private 
   def xyz2
     p "called"
   end

  protected
   def xyz3
     p "called"
   end

 end

 a=Abc.new
 b=Xyz.new
 a.abc(b)

在这里,我可以在对象a上调用obj b的xyz1方法。但是我无法在对象a上调用obj b的受保护xyz3和私有方法xyz2方法。“它永远不可能直接访问另一个对象的私有方法,即使对象与调用者属于同一类。“这究竟意味着什么?

任何人都可以通过更好的例子帮助我理解这个例子????

3 个答案:

答案 0 :(得分:5)

私人方法

要定义私有方法,我们使用private关键字,它实际上是在名为Module的类中实现的内置方法。私有方法只能由定义它的类(或其子类之一)中的另一个方法调用。

class Koan
  def call_say_koan
    say_koan
  end

  private
    def say_koan
      puts "What is the sound of one hand clapping?"
    end
end

k = Koan.new
k.say_koan    # Output: NoMethodError: private method `say_koan' called for #<Koan:0x000000021e7380>
k.call_say_koan        # Output: What is the sound of one hand clapping?

在上面的例子中,我们无法直接调用say_koan私有方法(来自类外),但我们可以调用call_say_koan公共方法,而后者又称为say_koan。

同样在上面的例子中,内置的私有方法没有参数。因此,下面定义的所有方法都是私有的。

私有方法也可以与先前定义的方法名称(作为符号传递)一起用作参数。

class Foo
  def some_method
  end

  private :some_method
end

为了使类方法变为私有,请使用private_class_method关键字/方法而不是私有。

无法使用接收器调用私有方法,例如self。尝试在call_say_koan中使用self作为接收者(self.say_koan)调用say_koan方法将导致以下异常:

NoMethodError: private method `say_koan' called for #<Koan:0x000000021eb548>

从Ruby 2.0开始,respond_to?当私有方法作为参数时,method将返回false。

k.respond_to? :say_koan  # Output: => false

要列出类中的所有私有实例方法,请使用private_instance_methods内置方法。对于私有类方法,请使用private_methods。

Koan.private_instance_methods(false)  # Output => [:say_koan]

受保护的方法

要定义受保护的方法,我们使用protected关键字(实际上是一个方法)。与私有方法一样,受保护的方法也可以由定义它的类(或其子类之一)中的其他方法调用。不同的是,受保护的方法也可以在同一个类的其他实例中调用。

没有受保护的类方法,Ruby只支持受保护的实例方法。

假设我们需要选择一些冥想者参与一项研究。为了找到经验最丰富的冥想者,我们需要比较他们的冥想总时数。但是,我们不希望看到小时数。

class Meditator
  def initialize(hours)
    @hours = hours
  end

  def more_experienced?(other_person)
    hours > other_person.hours
  end

  protected
    attr_reader :hours  # We have made the accessor protected
end

m1 = Meditator.new 3000
m2 = Meditator.new 5000

m2.more_experienced? m1  # Output: => true
m1.more_experienced? m2  # Output: => false

类似的代码可用于保护任何类型的敏感数据免受外部访问(类及其实例之外),尽管Ruby中不常采用受保护的方法。

当没有参数调用时(如上例所示),受保护的方法将其下面定义的所有方法转换为受保护的方法。它还可用于保护以前定义的方法,如以下示例所示。

class Foo
  def some_method
  end

  protected :some_method
end

要列出类中的所有受保护实例方法,请使用protected_instance_methods内置方法。对于受保护的类方法,请使用protected_methods。

Meditator.protected_instance_methods(false)  # Output: => [:hours]

答案 1 :(得分:4)

考虑受保护和私有方法的最佳方式是它们如何影响您是否可以在方法调用之前明确指定接收方,即将其视为技术规则,而不是某种元思想。

  1. 私有方法:您无法在私有方法调用之前显式指定接收方。 Ever (见评论)。因为ruby总是使用某个接收器调用方法,所以ruby使用当前分配给self变量的任何对象作为接收者。

  2. 受保护的方法:在某些情况下,您可以明确指定受保护方法调用的接收方。

  3. 然后,只需要探索ruby何时允许您在受保护的方法调用前明确指定接收器:

    class Dog
      def execute_pro(dog)
        dog.pro_meth
      end
    
      protected
      def pro_meth
        puts 'protected'
      end
    end
    
    d1 = Dog.new
    p d1.protected_methods
    d1.pro_meth
    
    --output:--
    [:pro_meth]
    
    1.rb:15:in `<main>': protected method `pro_meth' called for #<Dog:0x007f8ef90e0eb8> (NoMethodError)
    

    上面的例子证明了声明:

      

    如果方法受到保护,则可以由任何实例调用   定义类或其子类。

    太笼统了。但是:

    class Dog
      def execute_pro(dog)
        dog.pro_meth
      end
    
      protected
      def pro_meth
        puts 'protected'
      end
    end
    
    d1 = Dog.new
    d2 = Dog.new
    p d1.protected_methods
    
    d1.execute_pro d2
    
    --output:--
    [:pro_meth]
    protected
    
      

    永远不可能直接访问另一个对象的私有方法,即使该对象与调用者属于同一个类。

    在上面的例子中,调用者是d1,并且在d1 调用的方法中的你可以直接访问d2的受保护方法,即你可以在受保护的方法调用前明确指定接收器,因为d2与d1属于同一类。您将无法直接访问d2的私有方法 - 因为无法无法在私有方法调用之前明确指定接收方。这可能会让你相信......&#34;好吧,私有和受保护是一回事:如果方法是私有的,我只需要删除显式接收器:

    class Dog
      def execute_pro(dog)
        pro_meth #<*****CHANGE HERE
      end
    
      private   #<****CHANGE HERE
      def pro_meth
        puts 'protected'
      end
    end
    
    d1 = Dog.new
    d2 = Dog.new
    
    d1.execute_pro d2
    
    --output:--
    protected
    

    输出是相同的...但结论,&#34;私有和受保护是一回事:如果方法是私有的,我只需要删除显式接收器。&#34;是错的。这个例子太简单了,它掩盖了私有和受保护之间的细微差别。这是一个更好的例子:

    class Dog1
      def initialize(num)
        @num = num
      end
    
      def execute_pro(dog)
        dog.pro_meth 
      end
    
      protected  
      def pro_meth
        puts "protected -> #{@num}"
      end
    end
    
    d1 = Dog1.new 1
    d2 = Dog1.new 2
    
    d1.execute_pro d2
    
    class Dog2
      def initialize(num)
        @num = num
      end
    
      def execute_pro(dog)
        pro_meth  #<****CHANGE HERE
      end
    
      private  #<******CHANGE HERE
      def pro_meth
        puts "protected -> #{@num}"
      end
    end
    
    d1 = Dog2.new 1
    d2 = Dog2.new 2
    
    d1.execute_pro d2
    
    --output:--
    protected -> 2
    protected -> 1
    

    输出显示您不能只将受保护的方法转换为私有方法,并从方法调用中删除显式接收器,并始终获得相同的结果。在这种情况下,ruby允许您在受保护的方法调用之前显式指定接收器,以便让您将方法调用定向到正确的对象。但受保护的方法与公共方法不同:

    class Dog
      def execute_pro(dog)
        dog.pro_meth
      end
    
      protected
      def pro_meth
        puts 'protected'
      end
    end
    
    class Poodle < Dog
      def dostuff(dog)
        dog.pro_meth
      end
    end
    
    class Cat
      def dostuff(dog)
        dog.pro_meth
      end
    end
    
    d1 = Dog.new
    p1 = Poodle.new
    c1 = Cat.new
    
    p1.dostuff d1
    c1.dostuff d1
    
    --output:--
    protected
    
    1.rb:21:in `dostuff': protected method `pro_meth' called for #<Dog:0x007fe9040d28f8> (NoMethodError)
        from 1.rb:30:in `<main>'
    

    在行c1.dostuff d1中,调用者是c1,而在c1调用的内部方法中,你不能调用Dog的受保护方法 - 因为Cat类不是同一个类或者是子类的狗班。另请注意,如果您尝试删除显式接收器:

    class Cat
      def dostuff(dog)
        pro_meth  #<****CHANGE HERE
      end
    end 
    

    也不会调用Dog的受保护方法,因为在dostuff()内部,ruby将调用者分配给自变量,调用者是c1,所以你得到:

    class Cat
      def dostuff(dog)
        #self = c1
        pro_meth
      end
    end 
    
    ...
    c1.dostuff d1
    

    和ruby将方法调用pro_meth转换为self.pro_meth,相当于c1.pro_meth,而Cat类没有定义名为pro_meth的方法。

答案 2 :(得分:1)

你在某处选择的例子真的很混乱(特别是名字是abc或xyz,上下文非常少)。请允许我使用其他示例。

理论是这样的:

- 通过公共方法从课堂外访问私人和受保护。

受保护和私密之间的区别是:

- 无法使用接收器调用私有方法(甚至不能使用#self)。除非......调用私有的setter方法。如果您尝试删除接收器,Ruby将创建一个局部变量。在这种情况下,自我是必须的。

- 受保护可能会也可能不会使用#self等接收器。

- 受保护的可以访问来自同一个类的另一个对象的受保护方法,Private不能。 (参见#eat_more_than(其他)方法的代码)

现在谈到继承...

- 私有方法只能在子类上隐式调用(只是方法的名称),而不是显式调用(使用#self)。

- 可以双向调用受保护的方式(隐式或显式地使用或不使用#self ||)。

让我们来看看下面的例子:

 class Dog
  attr_accessor :name, :age

  def initialize(n, a)
    self.name = n
    self.age = a
  end

  def accessing_private
    "#{self.name} in human years is #{human_years}. This is secret!"
  end

  def accessing_protected
    "Will this work? " + a_protected_method
  end

  def eat_more_than(other) 
  # accessing other instance's protected method from the same class
    daily_diet < other.daily_diet 
    "#{name} eats more than #{other.name}"
  end

  def boy 
    gender_method("boy") # accessing private setter method
  end

  protected

  def daily_diet 
    age * 2 # the younger, the more they have to eat 
  end

  def a_protected_method
    "Yes, I'm protected!"
  end

  private

  attr_writer :gender

  def gender_method(gender)
    self.gender = gender # private setter method requires self
    "#{name} is a #{gender}"
  end

  def human_years
    age * 8
  end
end

# Create the first object of Dog
blake = Dog.new("Blake", 5)

p blake.accessing_private # "Blake in human years is 16. This is secret!"

p blake.accessing_protected # "Will this work? Yes, I'm protected!"

# Create the second object of Dog
jackson = Dog.new("Jackson", 1)

# Below, protected methods from different objects of the same type/class 
# are proven to share access
p jackson.eat_more_than(blake) # true -> "Jackson eats more than Blake"

# Below, accessing private setter method through a public method.
p blake.boy # Blake is a boy 

现在在一个子类上,你不能用接收器调用一个继承的私有方法,但是受保护的方式会很好(有或没有接收者):

class Puppy < Dog 

  def accessing_protected_explicitly
    "Explicitly calls '#{self.a_protected_method}'"
  end

  def accessing_protected_implicitly
    "Implicitly calls '#{a_protected_method}'"
  end 

  def accessing_private_implicitly 
    "#{self.name} is #{human_years} years old in human years. This is a secret!" # implicit
  end 

  def accessing_private_explicitly
    "#{self.name} is #{self.human_years} years old in human years" # explicit -> error
  end 
end 

# Below, testing them on a subclass 
booboo = Puppy.new("Booboo", 1 )
p booboo.accessing_protected_explicitly # works 
p booboo.accessing_protected_implicitly # works
p booboo.accessing_private_implicitly # works 
p booboo.accessing_private_explicitly # error, called on a receiver

我希望有所帮助!