CSC/ECE 517 Fall 2007/wiki1 4 01
The Question
Ruby's private methods can be called only on "self" (the calling object). They can't be applied to other objects of the same class, even from within the class definition. In class today, I opined that it is rarely if ever necessary to call a private method on another object of the same class. Try to find Java or C++ examples on the Web where a private method is called on another object. Decide if this is good design, or if it would be better performed by using accessor functions, from the standpoint of elegance or maintainability. If you cannot find any such examples (or even if you can), try to find arguments about why one should not invoke private methods on other objects. Analyze these arguments; can you find weaknesses?
Understanding the Question
In some languages such as Java and C++, it is legal to call the private methods on another object of the same class. However, in Ruby, the private methods can be called only on "self", implicitly. Did the authors of Ruby deliberately disable this functionality? If yes, why? What gain or drawback is there to nullify this feature and is it worth it?
Access Control
Object-oriented programming supports three levels of access control: public, protected and private.
- Public: the member variables and methods which are defined as public in a class can be called by anyone, including the object of the class and the classes inheriting from it.
- Protected: the visibility of member variables and methods which are defined as protected in a class is restricted, they can only be called by the class itself, the object of the defining class and subclasses inheriting from it.
- Private: the access of member varialbes and methods which are defined as private in a class is restricted only in the class itself. They can only be called by the methods which are part of the same class.
Example
Java example [3]:
public class A { private int x; protected int z; public A() { x = 10; z = 15; } public int getX() { return x; } public int getZ() { return z; } } public class B extends A { private int y; public B() { super(); // calls A constructor y = 20; } public void display() { System.out.print("X: " + getX() + " "); System.out.print("Y: " + y + " "); System.out.println("Z: " + z); }
Accessor
Most of the time, member variables are labeled as private. In some case, we might need to access the values of the member variables outside of the class definition. Instead of making them public, which violates the encapsulation of OO philosophy, we can provide functions that return the values of the member variables, known as accessors. For example, in the above code segment, methods getX() and getZ() are accessors.
Private Method in Ruby
In Ruby, invoking private methods on another object is forbidden, even if that object of of the same class as the current object. The private methods can only be invoked on the current object of the class.
for example:
class Person private def getAge return age; end def too_old( other_oject ) ( getAge > 30 ) ? true : false # correct ( self.getAge > 30 ) ? true : false # ERROR! ( other_object.getAge > 30 ) ? true : false # ERROR! end end
In the above code, getAge
is defined as a private method. self and other_object are the "receivers" of that method. The first statement in the method is therfore legal as the receiver of the call to getAge
is not only self but also implicit. The latter two violate this rule and errors will be raised.
Compared with Java
In Java or C++, it is perfectly legal to invoke private methods of another object of the same class as long as the invocation takes place within the class definition.
Take the following Java code for example:
public class Person { private double age; private double getAge() { return this.age; } public boolean isOlder( Person p ) { return ( this.age > p.getAge() ) ? true : false; } }
In the above code, we assume the member variable age
of the class Person
is sensitive and no accessor function of this variable should be provided. The method isOlder
takes a Person
object as a parameter. It compares the age of the current object to the age of the object p
passed in. Then isOlder
invokes p's private method getAge
to make the comparison. This invocation would be valid because it is from within the class definition. However, we can simply substitute p.getAge()
with p.age
due to the exactly same reason.
As the Ruby code shown above, receiver of a private method is always implicit, in other words, receiver in this case is always "self" but cannot be explicitly written out (i.e. self.getAge is not allowed). Of course, explicit invocation of a private method on another object is banned, even if it is of the same class as the current object and the invocation takes place within the class definition. In both cases, a "NoMethodError" exception would be raised.
In object-oriented languages, the private modifier is used as a mechanism to keep the private information of an object(i.e. its member variables) from being accessed by other parties. In our point of view, the only time that a method would want to call some other object's private method is to obtain the object's relevant information implicitly or explicitly. Otherwise, either the current object can simply invoke its own private method to perform the same functionality or that private method can be as well made public (i.e. there is no sensitive information needs to be protected). If indeed the private method provides some kind of access to the values of private member variables, an accessor function would be a much more elegant and maintainable solution. An accessor has a more descriptive method name and is therefore more readable. Even if in some cases, due to privacy reasons we do not want to provide accessors at all, the methods within the class definition can directly use another object's private member variables if needed, instead of having to invoke its private method to achieve the same purpose.
Therefore, considering the event of "invoking private methods of another object" rare and redundant in Java and C++, it is wise for Ruby to get rid of this capability all at once by enforcing the the receiver of a private method call to be implicit.
References
1. http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Classes#Private
2. http://en.wikipedia.org/wiki/Class_%28computer_science%29#Private.2C_protected.2C_and_public
3. http://www.mines.edu/Academic/courses/math_cs/macs262/INFO/Classes_in_Java_and_C++.html#Inheritance
4. Programming Ruby: The Pragmatic Programmers' Guide (Pragmatic Programmers Series) by Dave Thomas, Andy Hunt, Andrew Hunt, Chad Fowler, Chad Fowler [4]