In the last blog post, we introduced the concept of Object-oriented programming in Ruby, talked about the different programming paradigms, and created our first Class and Objects. To recap the code we wrote in that blog post, we finished with something like this.
#1. Class declaration
class Person
# 2. Behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
#3. Instantiation
p1 = Person.new
p2 = Person.new
p3 = Person.new
#4. Printing out and calling "greet" class method
puts p1.greet
puts p2
puts p3.greet
As we can see, we have 4 steps.
- * Step #1 - Class declaration. Is where we give a name to the class, and we open the class block
- * Step #2- We have a method. This means the behavior or logic to apply in each instance that we'll be creating outside the class
- * Step #3- Instantiation. Once we close the class declaration in a block, we start to create the instances of the class. All instances are defined by the class name followed by the keyword ".new"
- * Step #4- Finally, we call the class method using the variable assigned the instantiation.
As you can see, we're following the order given by the steps. And that's good; it helps us keep in mind the fundamental behavior of the classes and objects. Remember that the objects are the instances (a synonym inside the programming word). Now let's see something more about the OOP in Ruby.
Functions vs. Methods
Now that we know more about OOP and
Ruby functions is time to see what's a method in Ruby. Actually, it is the same as functions, but slightly different.
* A method is a function wrapped inside a class. A class is how we declare objects in OOP in Ruby. It contains the expected behavior of the class
* Functions are declared outside classes. It contains the expected behavior of part of the program
In the last exercise, we have a method called "greet."
class Person
….
def greet
"Hello world!"
end
….
end
Then outside the class, we called it after instantiation as follows:
p1 = Person.new
puts p1.greet
To call a method on an object (or instance), you must add a dot following the method name (p1.greet). These methods are also known as instance methods because the only way to invoke them is on the object.
The constructor method
As you may already know, you can give any name to the methods (also to the functions). However, inside the Ruby classes, we'll have a method known as the "constructor" method. This method has to be declared with a particular name called "initialize" and, like any other method (or function), can receive or not parameters.
This concept tends to confuse beginners, but it is not more than "preload" or having a method ready for each method instance. So, for example, let's say that each time you need a new object (or instance), you need some preloaded or default values, and these values could be different from object to object.
#1. Class declaration
class Person
#2. Constructor method called "initialize"
def initialize(name)
puts "creating the person #{name}"
end
# 3. Behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
#4. Instantiation
p1 = Person.new("Ana")
p2 = Person.new("Daniel")
p3 = Person.new("Matz")
#5. Printing out and calling "greet" class method
puts p1.greet
puts p2
puts p3.greet
What do you see differently here? Please take a while to try to catch the differences.
In the second step, we now have the constructor with the method name initialize who receives a parameter called "name." Then that parameter is used in a string under the initialize method.
In the fourth step, we instantiate not the class as follows: p1 = Person.new("Ana"). Here is the thing that tends to confuse us. Why are we calling a parameter after the ".new"? Because we declare an initializer inside our class and receive a parameter, we have to pass that value when we do the instantiation. If we don't do that, we receive an error.
Now let's try the code in our console.
➜ blog_tutorials ruby oop.rb
creating the person Ana
creating the person Daniel
creating the person Matz
Hello world!
#<Person:0x00000000029d7888>
Hello world!
As you can see, now we have 3 new outputs.
creating the person Ana
creating the person Daniel
creating the person Matz
This is because we instantiated 3 times the class.
Awesome, now let's try it without passing the parameter.
#1. Class declaration
class Person
#2. Constructor method called "initialize"
def initialize(name)
puts "creating the person #{name}"
end
# 3. Behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
#4. Instantiation
p1 = Person.new
p2 = Person.new
p3 = Person.new
#5. Printing out and calling "greet" class method
puts p1.greet
puts p2
puts p3.greet
The class is exactly the same as before, but now, in Step #4, We try to instantiate without a parameter. We have the following error:
➜ blog_tutorials ruby oop.rb
oop.rb:4:in `initialize': wrong number of arguments (given 0, expected 1) (ArgumentError)
from oop.rb:15:in `new'
from oop.rb:15:in `<main>'
Now, as we mentioned before, the initialize method can come without a parameter; let's check
#1. Class declaration
class Person
#2. Constructor method called "initialize"
def initialize
puts "creating the person"
end
# 3. Behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
#4. Instantiation
p1 = Person.new
p2 = Person.new
p3 = Person.new
#5. Printing out and calling "greet" class method
puts p1.greet
puts p2
puts p3.greet
What do you notice differently? Try it by yourself. This code works now.
➜ blog_tutorials ruby oop.rb
creating the person
creating the person
creating the person
Hello world!
#<Person:0x0000000002553730>
Hello world!
Attributes (object information)
Just as an object has behavior, objects (or instances) can also have information or attributes. For example, a person can have a name, age, height, etc. These are the attributes. Think of attributes as variables that are associated with the object.
In Ruby, we will identify attributes in a class because they begin with the @ character. For example, we can store the argument coming from the constructor inside the Person attribute. Why is this important? Because once we have an instance variable in the constructor, we can use it in any other method. Let's see how
#1. Class declaration
class Person
#2. Constructor method called "initialize"
def initialize(name)
@name = name
end
# 3. Behavior or logic to apply in each instance
def greet
"Hello #{@name}!"
end
end
#4. Instantiation
p1 = Person.new("Ana")
p2 = Person.new("Daniel")
p3 = Person.new("Matz")
#5. Printing out and calling "greet" class method
puts p1.greet
puts p2
puts p3.greet
Let's execute the code
➜ blog_tutorials ruby oop.rb
Hello Ana!
#<Person:0x0000000000a6eca8>
Hello Matz!
What do you see differently?
In Step #2, we're now using an instance variable called @name and assigning the value we receive in the initializer parameter. Then in Step #3, We used that instance variable inside the "greet" method. This is amazing and is the actual usage of the initializer method: it helps us store preloaded or default values in any other method inside the class.
I think this is enough information to study in this blog post. Later we'll see more details about the visibility of the attribute.
I hope you learned a lot.
Thanks for reading!
Daniel Morales