CSC/ECE 517 Fall 2007/wiki1 4 01

From Expertiza_Wiki
Revision as of 01:30, 19 September 2007 by Zchen3 (talk | contribs)
Jump to navigation Jump to search

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?

Private Method

Object-oriented programming supports three level access control: public, protected and private.

  • Public: the members and methods which is defined as public in a class can be called by anyone, including the object of the class and the classes inherit from it.
  • Protected: the visibility of members and methods which is 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 inherit from it.
  • Private: the access of members and methods which is defined as private in a class is restricted only in the class itself. They can only be called by the methods which is 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);
        }

Private Method in Ruby

In Ruby, the usage of public and protected access levels are the same as that of C++ and Java, but the private level is different.

For example [1]:

class AcessPrivate
  def a
  end
  private :a # a is private method
      
  def acessing_private
    a              # sure! 
    self.a         # sure! same as above, because "self." prefix was implicit there
    other_object.a # nope, a is private, you can't get it (but if it was protected, you could!)
  end
end

In the code above, we can see that in Ruby, private methods can be called in the method which is part of the definition class and also can be called by object "self", but it can not be called by other objects even in the same class definition.

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.

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.

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. However, in Ruby 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 [1]
5.