Saturday, June 9, 2012

Ruby Public, Protected and Private Methods

Ruby gives three types of method visibility:
  1. Public methods can be called by everyone - no access control is enforced. A class's instance methods (these do not belong only to one object; instead, every instance of the class can call them) are public by default; anyone can call them. The initialize method is always private.
  2. Protected methods can be invoked only by objects of the defining class and its subclasses. Access is kept within the family. However, usage of protected is limited.
  3. Private methods cannot be called with an explicit receiver - the receiver is always self. This means that private methods can be called only in the context of the current object; you cannot invoke another object's private methods.
In Ruby, things are a bit different. Both private and protected methods can be called by any instance of the defining class and its subclasses. Inheritance plays absolutely no role in determining the visibility of a method. The difference instead is that private methods can never be called with an explicit receiver, even if the receiver is self. This means that it’s not possible to access another object’s private methods, even if the object is of the same type as the caller. A private method must be called from within the calling object.
Consider the following example:
class Person
  def public_method
    "public"
  end

  protected
  def protected_method
    "protected"
  end

  private
  def private_method
    "private"
  end
end

class SalesPerson < Person
  def check_protected_method_explicit_receiver
    "#{self.protected_method} method OK with explicit receiver"
  rescue
    "failure accessing protected method with explicit receiver"
  end

  def check_protected_method_implicit_receiver
    "#{protected_method} method OK with implicit receiver"
  rescue
    "failure accessing protected method with implicit receiver"
  end

  def check_private_method_explicit_receiver
    "#{self.private_method} method OK with explicit receiver"
  rescue
    "failure accessing private method with explicit receiver"
  end

  def check_private_method_implicit_receiver
    "#{private_method} method OK with implicit receiver"
  rescue
    "failure accessing private method with implicit receiver"
  end
end
The public method can of course be accessed from anywhere (in this case, outside the class with an explicit receiver) and both private and protected methods of Person will obviously raise a NoMethodError.
Person.new.public_method
=> "public"
Person.new.private_method
=> NoMethodError: private method `private_method' called for #
Person.new.protected_method
=> NoMethodError: protected method `protected_method' called for #
So the protected method cannot be accessed outside of the class or it’s subclass, but from within the subclass, using it with either an implicit or explicit receiver works fine:
SalesPerson.new.check_protected_method_explicit_receiver
=> "protected method OK with explicit receiver"
SalesPerson.new.check_protected_method_implicit_receiver
=> "protected method OK with implicit receiver"
The private method can also be called from the subclass, but note how it only works with an implicit receiver:
SalesPerson.new.check_private_method_explicit_receiver
=> "failure accessing private method with explicit receiver"
SalesPerson.new.check_private_method_implicit_receiver
=> "private method OK with implicit receiver"
This also means you can do stuff like:
class Person
  def ==(other)
    protected_method == other.protected_method
  end
end

x = SalesPerson.new
y = Person.new
x == y
=> true
We’re accessing the protected_method of another class instance that shares our type here, specifying an explicit receiver. If you were to try to use private_method instead, a NoMethodError would be raised. You could also just call other.send("private_method").

In summary, method visibility and access control can be a bit confusing at first, especially if you’re coming over to Ruby from some other OO language. If you’re still confused, there’s more information available here and here. Do yourself a favor and make sure you understand, cuz it’s important stuff!

No comments:

Post a Comment