CSC/ECE 517 Fall 2011/ch3 4b ms

From Expertiza_Wiki
Jump to navigation Jump to search

Lecture 5: Object Oriented Programming in Ruby

This wiki tries to cover the Object Oriented concepts in ruby.

Introduction

Lecture 5 <ref name="CSC517">OOP in Ruby, CSC 517 Lecture5</ref> of CSC 517 covered topics of object oriented programming in Ruby. Object oriented programming is a paradigm in which real world entities are represented as objects, where each object has its own attributes and set of functions that it can perform. This gives a data structure called an "object" that consists of data fields and operations. This paradigm presents features like encapsulation, data abstraction, inheritance and polymorphism.

This wiki page tries to cover the other OOP languages and their features in a succinct manner and talks about the features of Ruby in detail with examples. We have also tried to compare the various languages in terms of their features to provide a better understanding.

The lecture also covered the concepts of duck typing in Ruby, closures and currying. The latter two have not been included as per the instructions by the instructor.

Object oriented languages: An Overview

C++:

C++ is a statically typed, multi-paradigm programming language. It supports some features of object-oriented languages like encapsulation, polymorphism and inheritance. It provides access control by specifiers public, private and protected and also allows “friend” classes. C++ allows multiple inheritance which is eliminated in most other programming languages as it allows a class to inherit from more than one base class which results in an ambiguity effect called Diamond problem.

Java:

Java is classified as a statically typed, hybrid object oriented language. Although it provides information hiding, inheritance and polymorphism, it has a few basic types incorporated that are not objects and some of the built-in operators are provided by basic arithmetic and not as messages to objects.[1] For these reasons, it is not considered to be a “pure” object-oriented language.

Java provides access control and supports private, public and protected access. It also allows to create packages to create and maintain namespaces. Java is designed not to provide multiple inheritance to avoid problems faced in C++. However, it presents a single-rooted hierarchy where “Object” is the ancestor of all classes (seen in Ruby too) and provides interfaces to extend/inherit functionality.

Python:

Python is a dynamically typed, interpreted, object oriented language. It is argued that Python is not completely object oriented as it does not provide access control in the form of public, private or protected variables. It has different security mechanisms. However, it allows to emulate protected member by using the prefix '_' before the name and private member by the use of the prefix '__' (double underscore) before the name.

Ruby:

Ruby is dynamically typed and highly object-oriented, much more than Java and Python. In Ruby, it is said that “everything is an object”.  Every value happens to be an object in Ruby. You may call methods on a string, an integer just like you call on any object in Java. For ex,

    (2.1143).size.times{puts “Hello!”}

This example will display “Hello!” six times on your IRB as the size of the parameter "2.1143" is 6.

Comparison of object Oriented Languages

The following table compares the Properties of some of the well-known Object Oriented Languages.<ref name="comparisonOOP">Programming Language Comparison</ref>


Language Ruby Java C# C++ Python SmallTalk Perl
Object Orientation Pure Hybrid Hybrid Hybrid/Multi-Paradigm Hybrid Pure Add-on/Hybrid
Static/Dynamic Typing Dynamic Static Static Static Dynamic Dynamic Dynamic
Generic Classes N/A No No Yes N/A N/A N/A
Inheritance Single Class/Multiple Mixins Single Class/Multiple Interface Single Class/Multiple Interface Multiple Multiple Single Multiple
Feature Renaming Yes No No No No No NO
Method Overloading No Yes Yes Yes No No No
Operator Overloading Yes No Yes Yes Yes Yes Yes
Class Variables or methods Yes Yes Yes Yes No Yes No
Access Control Public, Protected, Private Public, Protected, Package, Private Public, Protected, Private, Internal, Protected Internal Public, Protected, Private, Friends Name Mangling Protected Data, Public Methods None

Ruby : Purely Object Oriented

Ruby is considered to be a purely Object oriented Language, as everything in it is considered to be an object. Unlike Java, even primitives such as characters, punctuation, numbers and strings are treated as objects in Ruby. Ruby was designed to facilitate the enforcement of Object Oriented Methods. Ruby is a Dynamically Typed Language.

Object oriented Concepts in ruby:

Ruby is a singly rooted Object Oriented Programming language which has all its classes having the same superclass which is the class Object.

Classes:

A class is used to define a blueprint of a data type. It does not contain any data, but it specifies what kind of variables and methods an object of the class will have. A class in Ruby is defined using the keyword class followed by the classname. The variables and methods are defined within the class definition and the class definition terminates with the end keyword.

Classes in Ruby












For ex:

 class Rectangle
    def initialize(w,l)
       @width, @length = w, l
    end
 end

Here, the initialize method is used to set the values of the instance variables (@width and @length. The instance variables are the properties of the objects created of this class type. These instance variables can be accessed within the class using an @. These variables can be accessed from outside the class using the accessor methods which are defined inside the class.

In the following case printLength and printWidth are public methods used to access the instance variables from outside the class. These are called accessor methods. Similarly, to

  class Rectangle
 # constructor method
 def initialize(w,l)
    @width, @length = w, l
 end
 # accessor methods
 def printWidth
    @width
 end
 def printLength
    @length
 end


 # setter methods
 def setWidth=(value)
    @width = value
 end
 def setHeight=(value)
    @height = value
 end
 end
  # create an object
    rectagle = Rectangle.new(10, 20)
   # use accessor methods
     x = rectangle.printWidth()
     y = rectangle.printLength()
     puts "Width of the Rectangle is : #{x}"
     puts "Length of the Rectangle is : #{y}"

Class Methods and Class Variables:

A class variable is shared between all instances of the class. The value of the class variable remains the same for all the objects of the class. A class variable is accessed within the class using two @ signs at the beginning of the variable name. The class variables must be initialized within the class.

The class methods are defined using def self.methodname() and ends with an end delimiter. These methods are called using the class name itself, for example: classname.methodname

Attributes and accessor methods:

Every class in Ruby defines a set of attributes for its objects. Ruby provides accessor methods (famously known as getter-setter methods in Java) to access these variables. Ruby provides three kinds of accessor methods:

   Class Car
      attr_accessor : name, company
      attr_reader: price
      attr_writer: color
   end

attr_accessor creates a setter and getter method. The attr_reader method creates only the getter method. This is required in case of attributes that are not to be modified by other programs but their value may be viewed. The attr_writer creates the setter method. In case of Java, the setter and getter methods need to be written in 3 lines of code each whereas in Ruby, the methods are written just by specifying the type of accessor, which have already been coded using Metaprogramming.


Inheritance

Inheritance is a relation between two classes. We know that all cats are mammals, and all mammals are animals. The benefit of inheritance is that classes lower down the hierarchy get the features of those higher up, but can also add specific features of their own.<ref name = "Inheritance in Ruby">Inheritance in Ruby </ref> In ruby, a class can only inherit from a single other class unlike some other languages.

Example of Inheritance:

  class Fruit
      def getcolour
        @colour
      end
  end
  class Apple < Fruit
     def getType
       @type
     end
  end
  
  greenapple = apple.new
  greenapple.getcolour 
  grenapple.getType

In the above example, the class apple has all the examples that the class Fruit has, in addition to the method getType. When the object calls the method getColour, it calls the method getColour of the superclass Fruit. When the object calls the method getType, it calls the method getType of the subclass Apple. In the above example, the class apple has all the examples that the class Fruit has, in addition to the method getType.

The subclasses can override the methods of the superclass by redifining the methods within their class definitions. This is done when the subclass of a superclass needs to add its own functionality to the inherited method which is more specific to the subclass.

Inheritance lets you create a class that is a refinement or specialization of another class.

Access Control:

The only easy way to change an object's state in Ruby is by calling one of its methods. Control access to the methods, and you have controlled access to the object. A good rule of the thumb is never to expose methods that could leave an object in an invalid state.

Ruby gives you three levels of protection:

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.

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.

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.

Access control is determined dynamically, as the program runs, not statically. You will get an access violation only when the code attempts to execute the restricted method. Ex:

   class Employee
      def initialize(name, age, salary)
          @name = name
          @age = age
          @salary = salary
      end
 
      def name
          @name
      end 
      def age
         @age
      end
      def salary
          @salary
      end
       public :name
       protected :age
       private :salary
     end
     emp = Employee.new('Jack', 25, 2000);
     puts emp.name
     #puts emp.age    #age protected. gives access violation error
     #puts emp.salary #salary private

Abstract methods

Ruby is a dynamically typed language and does not provide abstract classes as in Java or C++. It instead provides modules that can be included within any class. The reason why Ruby does not provide abstract classes is because of the idea that it only matters if two objects can perform a function, they don’t necessarily have to be inherited from the same class. This paradigm is provided by modules, where the method of a module can be redefined as required by a class that includes it.

Also, if required the technique of abstract class can be simulated by something like this:

 Class Vehicle
  def drive
  raise NotImplementedError.new("Method not implemented")
  end
 end

The subclasses that inherit from the above class will have to redefine the method “drive” as required.

Duck Typing in Ruby

Ruby provides unbounded polymorphism as a dynamically typed language through the concept of “Duck-typing”. “Duck typing is a style of dynamic typing in which an object's current set of methods and properties determines the valid semantics, rather than its inheritance from a particular class or implementation of a specific interface” <ref name="Duck typing">DuckTyping</ref>. This concept is explained by the famous definition as “When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.” This essentially means that in languages like Ruby, it is only checked if an object can implement a function and the type of the object is not taken into consideration. It can be explained better with the help of the following example

 class Lion
   def roar
       puts “The Lion roars when hungry”
   end
 end
 class Person
    def roar
       puts “The man roared in fury and anger”
    end
 end
  def quality lion
   lion.roar
  end
  def start
   leo = Lion.new
   jack = Person.new
   quality leo
   quality jack
  end
  start

In the above example, leo is an object of class “Lion” and the “roar” method defined in its class is invoked. jack is an object of class “Person” and the corresponding method defined in the Person class is called for him. Although in the real world, both objects are not of the same type, Ruby allows unbounded polymorphism as both of them implement a particular aspect.

Thus, the check for an invoked method is done at run-time in dynamically typed languages and the method defined on that object type is called. If the method is undefined, a “NoMethodError” exception is raised.

Duck Typing in Python

Python also heavily uses duck typing to allow flexibility to programmers. It emphasizes on the interface rather than on the specifc type which allows for polymorphic substitution<ref>Python Duck-typing</ref>. In Python, functions like type() or isinstance() is used to check for usual methods to match the type of find out if an object is an instance of a particular class. However, in case of duck typing, the focus is on the attribute of that object, so it checks for the presence of a particular attribute in that object using hasattr().

Example for duck typing in Python is where Python uses a “pickle” module to serialize and de-serialize an object. So, we can introduce a new class that creates a “Tape” object. Now, an object of this type can be passed to the pickle module. Python will not differentiate between a file object or a tape object as long as they both have read() and write() methods.<ref name="example">Ducktyping example in Python</ref>

  class Tapefile():
     “A tape file”
      def __init__(self,params):
         #various initialization for tape access
      pass
      def read(len):
         #use special methods to access tape
      pass
      def readline():
         #use special methods to read a whole line on tape
      pass
      def write(value):
         #use special methods to write on tape
      pass

Duck Typing in other languages

Duck typing is considered to be a feature provided by dynamic languages. C++ as a static language checks for methods by parameter and signature types. Java does not support duck typing either but it is argued that it can be simulated by the use of interfaces. The concept of duck-typing is criticized as it requires the programmer to have a very wide understanding of the source code and that two objects may implement a method in completely different contexts and so it may result in strange errors.

Although duck typing is considered to be a feature of dynamic languages, some static languages like C# and Boo support it by allowing type checking of classes by the compiler to occur at run time.

References

<references />