Web | This Site

Ruby Basics
Ruby Advanced
Ruby Useful References
Ruby Useful Resources
Selected Reading
© 2011 TutorialsPoint.COM

| Home |  | References |  | Discussion Forums |  | About TP

# Object Oriented Ruby

Ruby is considered a pure object-oriented language, because everything appears, to Ruby, as an object.

All Ruby data consists of objects that are instances of some class. Even a class itself is an object that is an instance of the Class class.

## Defining a Class:

Classes are created in Ruby with the class keyword:

    class Point [< superclass ]
      code
    end

Like most Ruby constructs, a class definition is delimited with an end. In addition to defining a new class, the class keyword creates a new constant to refer to the class. The class name and the constant name are the same, so all class names must begin with a capital letter.

Within the body of a class, but outside of any instance methods defined by the class, the self keyword refers to the class being defined.

## Instantiating an Object:

Even though we haven't put anything in our Point class yet, we can still instantiate it:

    p = Point.new

## Initializing a Point:

When we create new Point objects, we want to initialize them with two numbers that represent their X and Y coordinates. In many object-oriented languages, this is done with a constructor. In Ruby, it is done with an initialize method:

    class Point
      def initialize(x,y)
        @x, @y = x, y
      end
    end

Now we can create point as follows:

    p = Point.new(15,20)

NOTE: An object can call initialize on itself, but you cannot explicitly call initialize on p to reinitialize its state.

## Defining a to_s Method:

Any class you define should have a to_s instance method to return a string representation of the object. Here's how we might do this for Point:

    class Point
      def initialize(x,y)
        @x, @y = x, y
      end

      def to_s        # Return a String that represents this point
        "(#@x,#@y)"   # Just interpolate the instance variables
      end
    end

With this new method defined, we can create points and print them out:

    p = new Point(1,2)   # Create a new Point object
    puts p               # Displays "(1, 2)"

## Accessors and Attributes:

Here is the way we can define accessor methods which will return or set values of class attributes ( x and y in our example.

    class Point
      def initialize(x,y)
        @x, @y = x, y
      end

      def acces_x           # The accessor method for @x
        @x
      end

      def access_y           # The accessor method for @y
        @y
      end
    end

With these methods defined, we can write code like this:

    p = new Point(1,2)   # Create a new Point object
    q = p.access_x + p.access_y
    puts q               # Displays 3

## Operator Overloading:

We'd like the + operator to perform vector addition of two Point objects, the * operator to multiply a Point by a scalar, and the unary . operator to do the equivalent of multiplying by .1. Here is a version of the Point class with mathematical operators defined:

    class Point
      attr_reader :x, :y   # Define accessor methods

      def initialize(x,y)
        @x,@y = x, y
      end

      def +(other)         # Define + to do vector addition
        Point.new(@x + other.x, @y + other.y)
      end

      def -@               # Define unary minus to negate x and y
        Point.new(-@x, -@y)
      end

      def *(scalar)        # To perform scalar multiplication
        Point.new(@x*scalar, @y*scalar)
      end
    end

## Array and Hash Access with [ ]:

Ruby uses square brackets for array and hash access, and allows any class to define a [] method and use these brackets itself.

Let's define a [] method for our class to allow Point objects to be treated as read-only arrays of length 2, or as read-only hashes with keys :x and :y:

    # Define [] method to allow a Point to look like an array or
    # a hash with keys :x and :y
    def [](index)
      case index
      when 0, -2: @x    # Index 0 (or -2) is the X coordinate
      when 1, -1: @y    # Index 1 (or -1) is the Y coordinate
      when :x, "x": @x  # Hash keys as symbol or string for X
      when :y, "y": @y  # Hash keys as symbol or string for Y
      else nil  # Arrays and hashes just return nil on bad indexes
      end
    end

## A Class Method:

Let's take another approach to adding Point objects together. Instead of invoking an instance method of one point and passing another point to that method, let's write a method named sum that takes any number of Point objects, adds them together, and returns a new Point.

    class Point
      attr_reader :x, :y     # Define accessor methods

      # This return the sum of an arbitrary number of points
      def Point.sum(*points)
        x = y = 0
        points.each {|p| x += p.x; y += p.y }
        Point.new(x,y)
      end

    end

## Defining Constants:

Many classes can benefit from the definition of some associated constants. Here are some constants that might be useful for our Point class:

    class Point
      def initialize(x,y)  # Initialize method
        @x,@y = x, y
      end

      ORIGIN = Point.new(0,0)
      UNIT_X = Point.new(1,0)
      UNIT_Y = Point.new(0,1)

      # Rest of class definition goes here
    end

Inside the class definition, these constants can be referred to by their unqualified names. Outside the definition, they must be prefixed by the name of the class, of course:

    Point::UNIT_X + Point::UNIT_Y   # => (1,1)

## Class Variables:

Class variables are visible to, and shared by, the class methods and the instance methods of a class, and also by the class definition itself. Class variables have names that begin with @@. See the following example:

    class Point
      # Initialize our class variables
      @@n = 0              # How many points have been created
      @@totalX = 0         # The sum of all X coordinates
      @@totalY = 0         # The sum of all Y coordinates

      def initialize(x,y)  # Initialize method
        @x,@y = x, y       # Sets initial values

        # Use the class variables in this instance method
        @@n += 1
        @@totalX += x
        @@totalY += y
      end

      # A class method to report the data we collected
      def self.report
        # Here we use the class variables in a class method
        puts "Number of points created: #@@n"
        puts "Average X coordinate: #{@@totalX.to_f/@@n}"
        puts "Average Y coordinate: #{@@totalY.to_f/@@n}"
      end
    end

## Public, Protected, Private Methods:

Instance methods may be public, private, or protected.

- Public Methods: Methods are normally public unless they are explicitly declared to be private or protected. A public method can be invoked from anywhere, there are no restrictions on its use.

- Private Methods: A private method is internal to the implementation of a class, and it can only be called by other instance methods of the same class.

- Protected Methods: A protected method is like a private method in that it can only be invoked from within the implementation of a class or its subclasses. It differs from a private method in that it may be explicitly invoked on any instance of the class, and it is not restricted to implicit invocation on self.

These methods can be declared with three methods named public, private, and protected.. Here is the syntax

    class Point
      # public methods go here

      # The following methods are protected
      protected

      # protected methods go here

      # The following methods are private
      private

      # private methods go here
    end

Here is a class with a private utility method and a protected accessor method:

    class Widget
      def x                       # Accessor method for @x
        @x
      end
      protected :x                # Make it protected

      def utility_method          # Define a method
        nil
      end
      private :utility_method     # And make it private
    end

NOTE: The public, private, and protected apply only to methods in Ruby. Instance and class variables are encapsulated and effectively private, and constants are effectively public.

## Subclassing and Inheritance:

Most object-oriented programming languages, including Ruby, provide a subclassing mechanism that allows us to create new classes whose behavior is based on, but modified from, the behavior of an existing class.

When we define a class, we may specify that it extends or inherits from another class, known as the superclass. If we define a class Ruby that extends a class Gem, we say that Ruby is a subclass of Gem, and that Gem is the superclass of Ruby.

If you do not specify a superclass when you define a class, then your class implicitly extends Object. A class may have any number of subclasses, and every class has a single superclass except Object, which has none.

The syntax for extending a class is simple. Just add a < character and the name of the superclass to your class statement. For example, following define a class Point3D as a subclass of Point:

    class Point3D < Point
      code
    end

Now any instance of Point3D will inherit all methods of Point class and you can call them as follows:

    p2 = Point.new(1,2)
    p3 = Point3D.new(1,2)
    print p2.to_s, p2.class   # prints "(1,2)Point"
    print p3.to_s, p3.class   # prints "(1,2)Point3D"

## Overriding Methods:

When we define a new class, we add new behavior to it by defining new methods. Just as importantly, however, we can customize the inherited behavior of the class by redefining inherited methods. For example you can change the behavior of to_s as follows we replaced a comma with a hyphen :

    class Point3D < Point
      def to_s        # Return a String that represents this point
        "(#@x - #@y)" # Just interpolate the instance variables
      end
    end

    p2 = Point.new(1,2)
    p3 = Point3D.new(1,2)
    print p2.to_s, p2.class   # prints "(1,2)Point"
    print p3.to_s, p3.class   # prints "(1 - 2)Point3D"

## Inheriting Instance Variables:

Instance variables often appear to be inherited in Ruby. Consider this code, for example:

    class Point3D < Point
      def initialize(x,y,z)
        super(x,y)
        @z = z;
      end

      def to_s
        "(#@x, #@y, #@z)"
      end
    end

The to_s method in Point3D references the @x and @y variables from the superclass Point. This code works as you probably expect it to:

    Point3D.new(1,2,3).to_s  # => "(1, 2, 3)"

## Inheriting Class Variables:

Class variables are shared by a class and all of its subclasses. If a class A defines a variable @@a, then subclass B can use that variable. Although this may appear, superficially, to be inheritance, is it actually something different.

The following code demonstrates the sharing of class variables. It outputs 123:

    class A
      # A class variable
      @@value = 1

      # An accessor method for it
      def A.value; @@value; end
    end

     # Display value of A's class variable
    print A.value

    # Subclass alters shared class variable
    class B < A; @@value = 2; end

    # Superclass sees altered value
    print A.value

    # Another alters shared variable again
    class C < A; @@value = 3; end

    # 1st subclass sees value from 2nd subclass
    print B.value

## Inheriting Constants:

Constants are inherited and can be overridden, much like instance methods can. There is, however, a very important difference between the inheritance of methods and the inheritance of constants.

Point3D class can use the ORIGIN constant defined by its Point superclass. Where inheritance of constants becomes interesting is when a class like Point3D redefines a constant. A three-dimensional point class probably wants a constant named ORIGIN to refer to a three-dimensional point, so Point3D is likely to include a line like this:

    ORIGIN = Point3D.new(0,0,0)

Ruby issues a warning when a constant is redefined. In this case, however, this is a newly created constant. We now have two constants Point::ORIGIN and Point3D::ORIGIN.

## The Singleton Pattern:

A singleton is a class that has only a single instance. Singletons can be used to store global program state within an object-oriented framework and can be useful alternatives to class methods and class variables.

Properly implementing a singleton requires a number of the tricks shown earlier. The new methods must be made private and other standard methods like dup and clone must be prevented from making copies, and so on. Fortunately, the Singleton module in the standard library does this work for us. You just require singleton and then include Singleton into your class. This defines a class method named instance, which takes no arguments and returns the single instance of the class. Define an initialize method to perform initialization of the single instance of the class.

Following is a nice example to make a class singleton:

    require 'singleton'

    class PointStats
      include Singleton

      def initialize
        @n, @totalX, @totalY = 0, 0.0, 0.0
      end

      def record(point)
        @n += 1
        @totalX += point.x
        @totalY += point.y
      end

      def report
        puts "Number of points created: #@n"
        puts "Average X coordinate: #{@totalX/@n}"
        puts "Average Y coordinate: #{@totalY/@n}"
      end
    end

With a class like this in place, we might write the initialize method for our Point class like this:

    def initialize(x,y)
      @x,@y = x,y
      PointStats.instance.record(self)
    end

The Singleton module automatically creates the instance class method for us, and we invoke the regular instance method record on that singleton instance. Similarly, when we want to query the point statistics, we write:

    PointStats.instance.report

Advertisement

Online Image Processing

Indian Baby Names