CSC/ECE 517 Fall 2007/wiki1 5 ap: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 136: Line 136:
   takes away lot of error checking performed by the compiler and replaces it with unit testing to be  
   takes away lot of error checking performed by the compiler and replaces it with unit testing to be  
   performed by the programmer. However in most cases programmers prefer the compiler to find and help
   performed by the programmer. However in most cases programmers prefer the compiler to find and help
   avoid as many errors as possible before running their own unit tests, which can themselves be buggy
   avoid as many errors as possible before running their own unit test cases, which can themselves be  
   and incomplete.   
   buggy and incomplete.   


• The second drawback is the lack of a precise contract between the calling & the called methods. The
• The second drawback is the lack of a precise contract between the calling & the called methods. The
Line 145: Line 145:
   can get too verbose and less direct (informative). Also, there is a serious likelihood that it may not  
   can get too verbose and less direct (informative). Also, there is a serious likelihood that it may not  
   be in sync with the code, as the code evolves with time.
   be in sync with the code, as the code evolves with time.
</pre>
</pre>



Revision as of 20:31, 13 September 2007

What is duck typing?

"If it walks like a duck and quacks like a duck, it must be a duck" is the philosophy of duck typing.
In terms of Ruby, duck typing refers to the tendency to be less concerned with the class of an object and more concerned with what methods can be called on it and what operations can be performed on it. Duck typing allows an object to be passed in to a method that expects a certain type even if it doesn't inherit from that type. All it has to do is support the methods and properties of the expected type in use by the method.

Consider that we have two objects Circle and Square. These objects are clearly different from each other and clearly do not belong to the same class. Assume that they have some common methods like Area() and Perimeter(). Although these methods have the same names, their implementations are completely different. Now assume that we have a method DisplayDetails() which calls the methods Area() and Perimeter(). If we explicitly specify a type for the argument of DisplayDetails(), we will need two separate functions to display the area and perimeter of a Circle and a Square. But by using duck typing, we take off the argument type of DisplayDetails(), thereby enabling us to pass objects of either type and have their methods invoked.

Let's see a small example now of how duck typing is implemented in Ruby.

def get_successor(s)
raise ArgumentError, 'No successor method!' unless s.respond_to? :succ
return "Successor of #{s} is #{s.succ}"
end
puts get_successor('a') => Successor of a is b 
puts get_successor(4) => Successor of 4 is 5 


In this example, we define a method 'get_successor' which returns the successor of the object passed to it. All the objects on which the 'succ' method is defined can be passed as an argument to the 'get_successor' method irrespective of their class. Duck typing does not rely on inheritance and polymorphism. Every single object is accepted in every single method. The only thing that matters is the capability of that object to do whatever the method is trying to do. If the object is unable to do that, the interpreter happily raises an exception.


Duck typing v/s Interfaces

Advantages of Duck typing over Interfaces:

Consider the following example, in which the salary of an employee is calculated depending on his post and the number of days he has worked in the month. We will contrast the approaches of duck typing (Ruby) and interface (Java) one at a time.

Java Code:

package duck_typing;
interface getSalary
{
   public void compute(int days);
}

class Manager implements getSalary 
{
   public void compute(int days) 
   {
      System.out.print("This manager has worked " + days + " days this month, "); 
      System.out.println("so his salary is $" + (double) days*150);
   }
}

class Clerk implements getSalary 
{
   public void compute(int days)
   {
      System.out.print("This clerk has worked " + days + " days this month, "); 
      System.out.println("so his salary is $" + (double) days*55); 
   }
}

public class Calculate 
{
   public static void main(String[] args)
   {
      getSalary[] emp = {new Manager(), new Clerk()}; 
      for (int i = 0; i < emp.length; i++)
         emp[i].compute(20); 
   }
}


Output:

This manager has worked 20 days this month, so his salary is $3000
This clerk has worked 20 days this month, so his salary is $1100

In the above code, we have created an interface 'getSalary' which has the abstract method 'compute'. The two classes Manager and Clerk need to implement this interface and define the method 'compute', to calculate the salaries of its objects. As can be seen from the code, we can compute the salaries of both the manager as well as the clerk, even though they belong to different classes.


Now let's see how the same can be implemented in Ruby using duck typing.

Ruby code:


class Manager
def compute(days)
print "This manager has worked #{days} days this month, " 
puts "so his salary is $#{days * 150}"
end
end

class Clerk
def compute(days)
print "This clerk has worked #{days} days this month, "
puts "so his salary is $#{days * 55}" 
end
end

emp = [Manager.new(), Clerk.new()]
emp.each {|em| puts em.compute(20) }


Output:

This manager has worked 20 days this month, so his salary is $3000
This clerk has worked 20 days this month, so his salary is $1100


It can clearly be seen that implementing the above code is much simpler and less verbose as compared to java. In Ruby, we just created two classes, Manager and Clerk which implemented the method 'compute'. We were able to invoke the 'compute' method with the objects of different classes.
Thus it can be said that duck typing is a big time saver when we write codes, as the verification that the object does respond to different methods is not made when the object is passed as an argument to the method, but is made on the invocation of the said methods. And this is the reason why arguments to methods are usually not typed all.


We can sum up the advantages of duck typing as follows:

• Granularity: Duck-typed functions are the least dependent on the types of the arguments passed.On 
  the contrary, interfaces may carry extra requirements, like some methods that are not required for
  the function in question. Although we can split the interfaces by keeping only a certain number of
  required functions in them, the resulting increase in the number of interfaces can cause confusion.

• Adaptability: Thanks to the independence of duck-typed functions from the type of arguments passed, 
  a duck-typed function can be adapted to contexts that it was not built for initially, with a minimal 
  effort on the part of the caller.

• Sheer convenience: Since the types of arguments of methods are not typed, it eliminates the need to 
  write separate caller functions based on the type of arguments to be passed.


Disadvantages of Duck typing over interfaces:

• The first drawback of duck-typing is the lack of compiler safety net. If enough care is not taken 
  while writing a piece of code, there is little guarantee that it will run successfully. The 
  problem isn't with duck typing, but is in testing the 'duckiness' of an object. Duck typing
  takes away lot of error checking performed by the compiler and replaces it with unit testing to be 
  performed by the programmer. However in most cases programmers prefer the compiler to find and help
  avoid as many errors as possible before running their own unit test cases, which can themselves be 
  buggy and incomplete.  

• The second drawback is the lack of a precise contract between the calling & the called methods. The
  only way to find it out is to analyze the body of the called method, which can be impractical at 
  times, especially when we deal with a third-party API. The most common approach to mitigate the issues
  caused by this is to provide documentation. But even that is not really advisable, as the documentation
  can get too verbose and less direct (informative). Also, there is a serious likelihood that it may not 
  be in sync with the code, as the code evolves with time.

See also

http://cdsmith.twu.net/types.html
http://www.akropolix.net/rik0/blogs/category/programming/ruby/
http://www.oopcenter.com/article/ruby-on-rails/ruby-classes-and-objects.html
http://zachloch.wordpress.com/2006/07/04/duck-typing-dichotomy/

References

URL: http://www.alittlemadness.com/?p=28