After a while of evaluation I started over programming mainly in Ruby about two month ago.
Long time ago I learned for a short while object orientation programming in Smalltalk-80 but soon started to use C++, first with the AT&T preprocessor and later with the gcc. Since 1998 I am programming Java. All these years have left me with a certain idea of practical object orientation which was not really what I learned from Smalltalk-80.
But Ruby changed things a lot as I have to admit. In Ruby everything is an object and Ruby is interpreted like Smalltalk! What are the consequences. This is what my post will try to figure out.
From Ruby Core – Class documentation:
Classes in Ruby are first-class objects—each is an instance of class Class.
In Ruby everything is an object. This includes as well Integers as Classes. So what happens if you write something like:
a = "test1" b = "test2" |
You get references a and b to your Objects a and b of class String which is an object too and has the class object Object as superclass. So far the only difference is that the Classes are realized as objects themselves.
But you can do things in Ruby which you cannot do in C++ or Java. For example you can add a method only to one object like:
c = 'test' class < < c def double self + self end end c.double =>"testtest" |
This piece of code generates a virtual class exclusively for the object c.
What happens if you try the same for integers? If you try the same for integers it should look like object b in this graph:
The code for this would be:
b = 7 class < < b def double self + self end end TypeError: no virtual class for Fixnum |
So what is this? I thought that all objects can have virtual (or singleton) classes which hold the methods specially attached to this object?
The solution is quite simple. There are special classes like Fixnum which are called eigenclasses. Instead of a reference to an object these classes return the class object itself (for example as parameter for a method call). Eigenclass objects only have one single Object for example for each Fixnum value. This is why it is not possible to create singleton classes for eigenclass objects.
But what you can do is to extend an eigenclass with your own attributes like:
class Fixnum attr_accessor :name end b = 5 b.class => Fixnum b.name 'five' => "five" |
The next thing I want to do is to define a class variable. I found a nice article about class variables in Ruby on John Nunemakers blog. At first I was really astonished about this:
class Polygon @@sides = 10 def self.sides @@sides end end puts Polygon.sides # => 10 |
This is a Polygon class with a class variable which holds the number of sides of the Polygon and a getter class method. Now we create a subclass which inherits from Polygon and holds its own number of sides:
class Triangle < Polygon @@sides = 3 end puts Triangle.sides # => 3 puts Polygon.sides # => 3 |
What happens here? The Polygon sides have been set to 10 originally.
To understand this behavior you must have an understanding about the Ruby Meta-class and look-up mechanism for methods and variables. When you create a class variable or method Ruby actually creates the following Meta-Class for you:
The Meta-Class hold the Class level extensions of your class. If you subclass Polygon now you have the following image:
In Ruby everything, including class definitions, are executable code. The assignment to the class variable @@sides in the class Triangle leads the Ruby interpreter to search for an existing class variable @@sides which he finds in the Meta-Class of the superclass Polygon first and makes an assignment to this variable instead of creating a new meta-Class with the same variable for Triangle.
So what can we do if we want to have a variable for the sides of our class which is unique to the according class? As the class itself is an object you can assign a variable to the class object
You can do this with the following code:
class Polygon @sides class < < self attr_accessor :sides end end |
for the Class Polygon and the same for class Triangle. Be aware that you have to assign the accessors to the class object and not to the object itself.
I hope that my explanations are correct and can help other newbies in Ruby to understand the Class handling a little bit.