Before I read this article, I thought access control in Ruby worked like this:
public
- can be accessed by any object (e.g.Obj.new.public_method
)protected
- can only be accessed from within the object itself, as well as any subclassesprivate
- same as protected, but the method doesn't exist in subclasses
However, it appears that protected
and private
act the same, except for the fact that you can't call private
methods with an explicit receiver (i.e. self.protected_method
works, but self.private_method
doesn't).
What's the point of this? When is there a scenario when you wouldn't want your method called with an explicit receiver?
Private methods in Ruby:
If a method is private in Ruby, then it cannot be called by an explicit receiver (object). It can only be call implicitly. It can be called implicitly by the class in which it has been described in as well as by the subclasses of this class.
The following examples will illustrate it better:
1) A Animal class with private method class_name
In this case:
2) A subclass of Animal called Amphibian:
In this case:
As you can see, private methods can be called only implicitly. They cannot be called by explicit receivers. For the same reason, private methods cannot be called outside the hierarchy of the defining class.
Protected Methods in Ruby:
If a method is protected in Ruby, then it can be called implicitly by both the defining class and its subclasses. Additionally they can also be called by an explicit receiver as long as the receiver is self or of same class as that of self:
1) A Animal class with protected method protect_me
In this case:
2) A mammal class which is inherited from animal class
In this case
3) A amphibian class inherited from Animal class (same as mammal class)
In this case
4) A class called Tree
In this case:
Part of the reason why private methods can be accessed by subclasses in Ruby is that Ruby inheritance with classes is thin sugarcoating over Module includes - in Ruby, a class, in fact, is a kind of module that provides inheritance, etc.
http://ruby-doc.org/core-2.0.0/Class.html
What this means is that basically a subclass "includes" the parent class so that effectively the parent class's functions, including private functions, are defined in the subclass as well.
In other programming languages, calling a method involves bubbling the method name up a parent class hierarchy and finding the first parent class that responds to the method. By contrast, in Ruby, while the parent class hierarchy is still there, the parent class's methods are directly included into the list of methods of the subclass has defined.
protected
methods can be called by any instance of the defining class or its subclasses.private
methods can be called only from within the calling object. You cannot access another instance's private methods directly.Here is a quick practical example:
some_method
cannot beprivate
here. It must beprotected
because you need it to support explicit receivers. Your typical internal helper methods can usually beprivate
since they never need to be called like this.It is important to note that this is different from the way Java or C++ works.
private
in Ruby is similar toprotected
in Java/C++ in that subclasses have access to the method. In Ruby, there is no way to restrict access to a method from its subclasses like you can withprivate
in Java.Visibility in Ruby is largely a "recommendation" anyways since you can always gain access to a method using
send
:Consider a private method in Java. It can be called from within the same class, of course, but it can also be called by another instance of that same class:
So -- if the caller is a different instance of my same class -- my private method is actually accessible from the "outside", so to speak. This actually makes it seem not all that private.
In Ruby, on the other hand, a private method really is meant to be private only to the current instance. This is what removing the option of an explicit receiver provides.
On the other hand, I should certainly point out that it's pretty common in the Ruby community to not use these visibility controls at all, given that Ruby gives you ways to get around them anyway. Unlike in the Java world, the tendency is to make everything accessible and trust other developers not to screw things up.
Comparison of access controls of Java against Ruby: If method is declared private in Java, it can only be accessed by other methods within the same class. If a method is declared protected it can be accessed by other classes which exist within the same package as well as by subclasses of the class in a different package. When a method is public it is visible to everyone. In Java, access control visibility concept depends on where these classes lie's in the inheritance/package hierarchy.
Whereas in Ruby, the inheritance hierarchy or the package/module don't fit. It's all about which object is the receiver of a method.
For a private method in Ruby, it can never be called with an explicit receiver. We can (only) call the private method with an implicit receiver.
This also means we can call a private method from within a class it is declared in as well as all subclasses of this class.
You can never call the private method from outside the class hierarchy where it was defined.
Protected method can be called with an implicit receiver, as like private. In addition protected method can also be called by an explicit receiver (only) if the receiver is "self" or "an object of the same class".
Summary
Public: Public methods have maximum visibility
Protected: Protected method can be called with an implicit receiver, as like private. In addition protected method can also be called by an explicit receiver (only) if the receiver is "self" or "an object of the same class".
Private: For a private method in Ruby, it can never be called with an explicit receiver. We can (only) call the private method with an implicit receiver. This also means we can call a private method from within a class it is declared in as well as all subclasses of this class.
The difference
self
. Even you cannot callself.some_private_method
; you must callprivate_method
withself
implied. (iGEL points out: "There is one exception, however. If you have a private method age=, you can (and have to) call it with self to separate it from local variables.")In Ruby, these distinctions are just advice from one programmer to another. Non-public methods are a way of saying "I reserve the right to change this; don't depend on it." But you still get the sharp scissors of
send
and can call any method you like.A brief tutorial
Then you can run
ruby dwarf.rb
and do this: