Getting Started With Ruby: Object-Oriented Approach
Written by Mike James   
Monday, 25 March 2013
Article Index
Getting Started With Ruby: Object-Oriented Approach
Accessors
Inheritance and Type

Accessors

You might now think that you can declare instance variable to be used as simple properties or fields that are accessible from outside of the object - but no. 

Ruby objects can only have methods.

That is you can't use something like p.x=10 after defining an instance variable @x. 

The solution to this lack of simple properties or fields is solved by the use of accessor methods. Ruby methods don't have to be called using brackets so it's entirely possible, and even desirable, to confuse the look of a method with a variable.

All you need for a "get" accessor is to define suitable methods called x and y as in:

def x
 @x
end

def y
 @y
end

To understand how these methods work you also need to know that as well as being an object nearly everything in Ruby is an expression and expressions always return a value.

This is a functional programming aspect of Ruby and there are lots more - so much so that it's fair to consider Ruby object oriented with functional programming bolted on. If you want to see some of the functional aspects of Ruby then see the next part of this Getting Started.

In most cases the value of something considered as an expression is the value of the last statement it contains. Hence just using @x and @y as the last statements is equivalent to using return @x and return @y - which is also valid Ruby i.e. you can write the return if it makes you feel happy.

With these definitions you can now write things like:

puts p.x
puts p.y 

and fully appreciate the fact that while p.x and p.y look like simple properties they are in fact method evocations i.e. x is a method that by definition returns the value stored in @x.

Once you have this idea the "get" accessor is written in a very similar way but using the assignment symbol in the method name!

def x=(v)
 @x=v
end
def y=(v)
 @y=v
end

As you don't need to use brackets in method calls you can now apparently assign to a property in the usual way:

p.x=3
p.y=4

In the introduction I said that Ruby was designed not to be surprising - personally I find this deliberate confusion between methods and simple properties pleasing. It looke like simple assignment but you can code the accessor functions to perform appropriate checking and processing. In other words its compact, easy to use but still powerful.

On a more advanced level you can define methods that redefine operators in much the same way including the [] array or hash access operator.

Class variables - static

The @ is used to define instance variables but the new class is also an object and it can have variables and methods of its own. These play the role of what would be called, in other languages, static variables or methods.  However things are a little more complicated than you might expect because there are two types of class variables. 

Any instance variables you create within the class i.e. outside a method "belong" to the class object.

For example:

class Point
@maxpoints=20

defines a class instance variable which isn't accessible from instance methods and isn't created anew when an instance is created, i.e. the variable belongs to the Point "class" object.

To access it you have to write class accessor methods. A class method is prefixed by "self" and this always resolves to the instance which in this case is the class Point. (You can use Point in place of self which makes it even more clear that you are adding a method to a specific instance.)

For example a get accessor is:

def self.maxpoints
 @maxpoints
end

and you can now write:

puts Point.maxpoints

and access the variable in exactly the same way within an instance method.

The disadvantage of class instance variables is that they can't be accessed by instance methods - they really do only belong to the class object. What this means is that class instance variables don't really serve the same purpose as static variables in other languages. For example you can't create an instance method that makes use of the @maxpoints variable. That is 

def max
 puts @maxpoints
end

followed by

p.max

doesn't display the value of @maxpoints as defined in the class object. 

To get around this problem Ruby adds - class variables. These are defined using @@ and they are static in the sense that they belong to the class and behave just like class instance variables but they can be accessed from an instance method. This is a confusion and it leads to problems when we come to consider inheritance. 

This is a sightly complex topic and one that often confuses Ruby programmers coming from other languages where the concepts of static or class variable is simpler.

You can also create a complete static class by defining nothing but class variables and methods.

Once again this makes it even clearer that the class object is just a standard instance object which just happens to have a new method to create additional instances.

Dynamic

Notice that all Ruby objects are dynamic and can have methods added at anytime. This is very similar to the way JavaScript handles objects. 

For example:

def Point.maxpoints=(v)
 @maxpoints=v
end

This adds a new method to the Point object even though it isn't inside a class…end block.

When you define a function you can always specify the 

In fact all a class…end block does is to add set self to the name of the class. Following this you can write things like:

def Point.maxpoints=(v) 
 @maxpoints=v
end

If you know JavaScript this should sound familiar. JavaScript doesn't support classes, only objects, and in a sense Ruby only provides objects with a neat method of creating instances from a "template" object, e.g. Point is a "template" object used to create instances via its new method.

As another example consider modifying a Point instance after it has been created:

p=Point.new(1,2)
def p.test

 "hello"

end

Following this you can now write

puts p.test

and you will see "hello". Notice that this method is added only to the instance p of the class Point. 

You can think of the class object literally as a template which is used to stamp out copies of itself via the new method. After this the new instance is just an object just as the class is "just an object". You can add methods to any object at any time simply by defining them and so the class object really just provides a sort of starting point, a minimal definition of the object it creates.

This is a very common and natural approach to object orientation in any dynamic, i.e. interpreted, language.



Last Updated ( Monday, 29 April 2013 )