Ruby Object-Oriented

Ruby is a pure object-oriented language, and everything in Ruby takes the form of objects. Every value in Ruby is an object, even the most primitive things: strings, numbers, and even true and false are objects. The class itself is also anobject, which isClassan instance of the Class class. This chapter will explain all the main features related to Ruby object-oriented programming.

Classes are used to specify the form of objects. They combine data representation and methods, organizing data into a neat package. The data and methods in a class are called members of the class.

Ruby Class Definition

When you define a class, you are actually defining a blueprint for a data type. This does not actually define any data, but rather defines what the class name means, that is, what the objects of the class will consist of and what operations can be performed on the object.

A class definition starts with the keywordclassand is followed bythe class name, and finally ends withenda separator to mark the end of the class definition. For example, we use the keyword class to define the Box class, as shown below:

class Box code end

By convention, the name must begin with a capital letter. If it contains multiple words, capitalize the first letter of each word, but with no separators between them (e.g., CamelCase).

Defining Ruby Objects

Classes provide the blueprint for objects, so basically, objects are created based on the class. We use thenewkeyword to declare objects of the class. The following statement declares two objects of class Box:

box1 = Box.new box2 = Box.new

The initialize Method

initializeThe method is a standard Ruby class method, serving as the class constructor, and is similar to theconstructorworking principle in other object-oriented programming languages. When you want to initialize some class variables while creating an object, the initialize method comes in handy. This method takes a series of parameters; like other Ruby methods, when using it, you must place thedefkeyword before it, as shown below:

class Box def initialize(w,h) @width, @height = w, h end end

Instance Variables

Instance variablesare class attributes. When objects are created using the class, they become the attributes of the objects. Each object's attributes are assigned separately and do not share values with other objects. Inside the class, these attributes are accessed using the @ operator; outside the class, they are accessed using methods calledaccessor methods, which areofpublicmethods. Below we take the class defined aboveBoxas an example, and use @width and @height as the instance variables of class Box.

class Box def initialize(w,h) #Assigning Values to Instance Variables @width, @height = w, h end end

Accessor (getter) & Setter (setter) Methods

To read variables defined in a class from outside the class, we can define accessor (getter) methods to access them. The following example demonstrates the usage of accessor methods:

Example

#!/usr/bin/ruby -w #Define the class class Box #Constructor def initialize(w,h) @width, @height = w, h end #Accessor methods def printWidth @width end def printHeight @height end end #Create an object and initialize the box's height and width box = Box.new(10, 20) #Use accessor methods x = box.printWidth() y = box.printHeight() puts "Box width : #{x}" puts "Box height : #{y}"

Try it »

When the above code is executed, it produces the following result:

盒子宽度 : 10
盒子高度 : 20

Similar to accessor methods used to access variable values, Ruby provides a way to pass parameters to variables defined in a class from outside the class, the so-calledsetter methods, defined as follows:

Example

#!/usr/bin/ruby -w #Define the class class Box #Constructor method def initialize(w,h) @width, @height = w, h end #Accessor methods def getWidth @width end def getHeight @height end #Setter methods def setWidth=(value) @width = value end def setHeight=(value) @height = value end end #Create objects box = Box.new(10, 20) #Use setter methods box.setWidth = 30 box.setHeight = 50 #Use accessor methods x = box.getWidth() y = box.getHeight() puts "Box width : #{x}" puts "Box height : #{y}"

Try it »

When the above code is executed, it produces the following result:

盒子宽度 : 30
盒子高度 : 50

Since both methods are very commonly used, Ruby definesattr_accessor :variable_name、attr_reader :variable_name、attr_writer :variable_namethree attribute declaration methods. Among them:accessor=reader+writer。

Also note: the variable name must be prefixed with:, and between variable names use,to separate.

Instance Methods

Instance methodsare defined in the same way as other methods, using thedefkeyword, but they can only be used through class instances, as shown in the example below. Their functions are not limited to accessing instance variables; they can also perform many other tasks as needed.

Example

#!/usr/bin/ruby -w #Define the class class Box #Constructor method def initialize(w,h) @width, @height = w, h end #Instance method def getArea @width * @height end end #Create objects box = Box.new(10, 20) #Call the instance method a = box.getArea() puts "Area of the box is : #{a}"

Try it »

When the above code is executed, it produces the following result:

Area of the box is : 200

Class Methods & Class Variables

Class variablesare variables shared by all instances of a class. In other words, class variables can be accessed by all object instances. Class variables are prefixed with two @ characters (@@). Class variables must be initialized in the class definition, as shown in the example below.

Class methodsare defined using thedef self.methodname()keyword, and class methods end with the end delimiter. Class methods can be called using theclassname.methodnameform with the class name, as shown in the example below:

Example

#!/usr/bin/ruby -w class Box #Initialize class variables @@count = 0 def initialize(w,h) #Assign values to instance variables @width, @height = w, h @@count += 1 end def self.printCount() puts "Box count is : #@@count" end end #Create two objects box1 = Box.new(10, 20) box2 = Box.new(30, 100) #Call the class method to output the box count Box.printCount()

Try it »

When the above code is executed, it produces the following result:

Box count is : 2

The to_s Method

Any class you define has anto_sinstance method to return the string representation of the object. The following is a simple example representing Box objects based on width and height:

Example

#!/usr/bin/ruby -w class Box #Constructor method def initialize(w,h) @width, @height = w, h end #Define the to_s method def to_s "(w:#@width,h:#@height)" #The string format of the object end end #Create objects box = Box.new(10, 20) #The to_s method is called automatically puts "String representation of box is : #{box}"

Try it »

When the above code is executed, it produces the following result:

String representation of box is : (w:10,h:20)

Access Control

Ruby provides you with three levels of instance method protection, namelypublic, private, or protected. Ruby does not apply any access control to instance and class variables.

  • Public methods:Public methods can be called by any object. By default, methods are public, except the initialize method, which is always private.
  • Private methods:Private methods cannot be accessed or viewed from outside the class. Only class methods can access private members.
  • Protected methods:Protected methods can only be called by objects of the class and its subclasses. Access can also be performed only inside the class and its subclasses.

The following is a simple example demonstrating the syntax of these three modifiers:

Example

#!/usr/bin/ruby -w #Define the class class Box #Constructor method def initialize(w,h) @width, @height = w, h end #Instance methods are public by default def getArea getWidth() * getHeight end #Define private accessor methods def getWidth @width end def getHeight @height end # make them private private :getWidth, :getHeight #An instance method for outputting the area def printArea @area = getWidth() * getHeight puts "Big box area is : #@area" end #Make the instance method protected protected :printArea end #Create objects box = Box.new(10, 20) #Call instance methods a = box.getArea() puts "Area of the box is : #{a}" #Try to call the protected instance method box.printArea()

Try it »

When the above code is executed, it produces the following result. Here, the first method call succeeds, but the second method causes a problem.

Area of the box is : 200
test.rb:42: protected method `printArea' called for #
<Box:0xb7f11280 @height=20, @width=10> (NoMethodError)

Class Inheritance

Inheritance is one of the most important concepts in object-oriented programming. Inheritance allows us to define a class based on another class, making it easier to create and maintain applications.

Inheritance helps with code reuse and fast execution. Unfortunately, Ruby does not support multiple inheritance, but Ruby supportsmixinsmixins. Mixins are like a specific implementation of multiple inheritance, where only the interface part is inheritable.

When creating a class, the programmer can directly specify that the new class inherits the members of an existing class, thus avoiding writing new data members and member functions from scratch. This existing class is calledbase class or parent class, and the new class is calledderived class or subclass。

Ruby also provides the concept of subclassing, which is inheritance. The following example explains this concept. The syntax for extending a class is very simple. Just add a < character and the name of the parent class to the class statement. For example, the following defines the class'sBigBoxYesBoxsubclass:

Example

#!/usr/bin/ruby -w #Define class class Box #Constructor method def initialize(w,h) @width, @height = w, h end #Instance method def getArea @width * @height end end #Define subclass class BigBox < Box #Add a new instance method def printArea @area = @width * @height puts "Big box area is : #@area" end end #Create object box = BigBox.new(10, 20) #Output area box.printArea()

Try it »

When the above code is executed, it produces the following result:

Big box area is : 200

Method Overloading

Although you can add new functionality in a derived class, sometimes you may want to change the behavior of a method already defined in the parent class. In this case, you can keep the method name unchanged and override the method's functionality, as shown in the following example:

Example

#!/usr/bin/ruby -w #Define class class Box #Constructor method def initialize(w,h) @width, @height = w, h end #Instance method def getArea @width * @height end end #Define subclass class BigBox < Box #Change the existing getArea method def getArea @area = @width * @height puts "Big box area is : #@area" end end #Create object box = BigBox.new(10, 20) #Use the overridden method to output the area box.getArea()

Try it »

The output of the above example is:

Big box area is : 200

Operator Overloading

We want to use the + operator to perform vector addition of two Box objects, use the * operator to multiply the width and height of Box, and use the unary operator - to negate the width and height of Box. Below is a version of the Box class with mathematical operator definitions:

class Box def initialize(w,h) #Initialize width and height @width,@height = w, h end def +(other) #Define + to perform vector addition Box.new(@width + other.width, @height + other.height) end def -@ #Define unary operator - to negate width and height Box.new(-@width, -@height) end def *(scalar) #Perform scalar multiplication Box.new(@width*scalar, @height*scalar) end end

Freezing Objects

Sometimes, we want to prevent objects from being changed. In Object, the freeze method can achieve this, effectively turning an object into a constant. Any object can be frozen by callingObject.freezefreeze. A frozen object cannot be modified, that is, you cannot change its instance variables.

You can use theObject.frozen?method to check whether a given object has been frozen. If the object is frozen, this method returns true; otherwise, it returns a false value. The following example explains this concept:

Example

#!/usr/bin/ruby -w #Define class class Box #Constructor method def initialize(w,h) @width, @height = w, h end #Accessor method def getWidth @width end def getHeight @height end #Setter method def setWidth=(value) @width = value end def setHeight=(value) @height = value end end #Create object box = Box.new(10, 20) #Let's freeze the object box.freeze if( box.frozen? ) puts "Box object is frozen object" else puts "Box object is normal object" end #Now try using the setter method box.setWidth = 30 box.setHeight = 50 #Use the accessor method x = box.getWidth() y = box.getHeight() puts "Width of the box is : #{x}" puts "Height of the box is : #{y}"

Try it »

When the above code is executed, it produces the following result:

Box object is frozen object
test.rb:20:in `setWidth=': can't modify frozen object (TypeError)
        from test.rb:39

Class Constants

You can define a constant inside a class by assigning a direct numeric or string value to a variable. Constants do not need to use @ or @@. By convention, constant names are capitalized.

Once a constant is defined, you cannot change its value. You can access the constant directly inside the class, just like accessing a variable, but if you want to access the constant outside the class, you must useclassname::constant::, as shown in the following example.

Example

#!/usr/bin/ruby -w #Define class class Box BOX_COMPANY = "TATA Inc" BOXWEIGHT = 10 #Constructor method def initialize(w,h) @width, @height = w, h end #Instance method def getArea @width * @height end end #Create object box = Box.new(10, 20) #Call instance method a = box.getArea() puts "Area of the box is : #{a}" puts Box::BOX_COMPANY puts "Box weight is: #{Box::BOXWEIGHT}"

Try it »

When the above code is executed, it produces the following result:

Area of the box is : 200
TATA Inc
Box weight is: 10

Class constants can be inherited, and can also be overridden like instance methods.

Creating Objects Using allocate

There may be a situation where you want to create an object without calling the object constructorinitialize, i.e., without using the new method to create an object. In this case, you can call allocate to create an uninitialized object, as shown in the following example:

Example

#!/usr/bin/ruby -w #Define class class Box attr_accessor :width, :height #Constructor method def initialize(w,h) @width, @height = w, h end #Instance method def getArea @width * @height end end #Create object using new box1 = Box.new(10, 20) #Create another object using allocate box2 = Box.allocate #Call instance method using box1 a = box1.getArea() puts "Area of the box is : #{a}" #Call instance method using box2 a = box2.getArea() puts "Area of the box is : #{a}"

Try it »

When the above code is executed, it produces the following result:

Area of the box is : 200
test.rb:14: warning: instance variable @width not initialized
test.rb:14: warning: instance variable @height not initialized
test.rb:14:in `getArea': undefined method `*' 
   for nil:NilClass (NoMethodError) from test.rb:29

Class Information

Ruby's self and Java's this have similarities, but are also quite different. In Java, methods are referenced within instance methods, so this generally points to the current object. Ruby's code executes line by line, so self has different meanings in different contexts. Let's look at the following example:

Example

#!/usr/bin/ruby -w class Box #Output class information puts "Class of self = #{self.class}" puts "Name of self = #{self.name}" end

Try it »

When the above code is executed, it produces the following result:

Class of self = Class
Name of self = Box

This means that the class definition can be executed by treating the class as the current object, and it also means that the methods in the metaclass and parent class are available during the execution of the method definition.

Other Extensions