Share

This is a continuation of the following series of posts
Quick Ruby Tutorials-1
Quick Ruby Tutorials-2

In the last post our Dog class had the abilities to eat, sleep, bark, shit etc.
The class definition for Dog class looked like

class Dog
 attr_reader :weight, :height, :color
 attr_accessor: name
 
 def initialize(weight = 1,height = 0.5, color = "white")
  puts "in initialize"
   @weight, @height, @color = weight, height, color
  end

 def bark
  puts "bhow bhow!!!"
 end

 def eat(food="bone")
  puts "I am eating #{food}"
 end

 def sleep
  puts "bbye, I am going to sleep"
 end

 def shit
  puts "aah! now I am hungry again"
 end
end

Let us extend it.
Besides other activities like sleeping, barking etc, our dog can now eat food.
On eating the food digestion should start. We make one more method for the same and call it in
the “eat” method.

class Dog
 
 def eat(food="bone")
  puts "I am eating #{food}"
  digest_food
 end

 def digest_food
  puts "digesting food"
 end
end

d = dog.new
d.eat
# I am eating bone
#digesting food

We could have written the code to digest food in the method “eat”, but we create a separate method called “digest” and call this method in “eat”. This is known as Single Responsibility Principle(SRP), ie each method should have only one responsibility. This helps to maintain the code.In future if the code changes, it is easier to change and test a small method which does only one thing. If we have a method which is big and performs many tasks at the same time then it is difficult to change and maintain the code.

Here we can directly call the digest_food method as shown.

d.digest_food # digesting food

This is not correct, digestion should only start after eating food. If we call digest_food directly then it can start even if the dog has not eaten the food.
Digesting food is an internal function of the dog which is triggered due to some other external event( here eating food).We should not be able to access digest_food method from outside the class.

For this we will make the digest_food method as private.
Private??
What are Private and Public Methods?
Private methods are ones which cannot be accessed/called from outside the class.
The methods which we were defining till now were public methods. By default all the methods of the class are public ie they can be accessed inside/outside the class. To make a method as private we need to add a keyword private as shown below.

class Dog

 private
 def digest_food
 end

 def another_method
 end

end

d = Dog.new
d.digest_food #error: private method digest food called
d.another_method #error: private method another_method called

As seen above, all the methods defined after the keyword private are private methods.

So our class definition looks like.

class Dog

 def eat(food="bone")
  puts "I am eating #{food}"
  digest_food
 end

 private

 def digest_food
  puts "digesting food"
 end
end

d = dog.new
d.digest_method #error: private method another_method called

What are Class methods and Instance Methods?
Whatever methods we were defining till now were instance methods. Like bark, eat, digest_food etc.
We called these methods with respect to the instance/object like.

dog.eat
dog.bark

As these methods are related to an instance of the class they are called instance methods.
We cannot call instance method with respect to the class.

dog = Dog.new
dog.weight #works
Dog.weight #error: NoMethod error

Class methods are ones which are called with respect to the class.
We have already come across a class method “new”. We don’t need to define “new” as Ruby gives this method for free with every class :) .

We do Dog.new, we cannot do dog.new.

Class variables and instance variables
Instance variables are variables which are linked to the instance/object of the class
Instance variables as we have seen start with @ sign. There is a separate copy of the instance variable for each instance. They are accessible within a class in all the instance methods. They are not accessible outside a class. They are the ones responsible to store the data for an instance/object.

Class variables are once which store the data at class level.
Only one copy of the class variable is created for a class.
Class variables can be accessed in class as well as instance method.

There is a separate copy of instance variable for for every instance, but there is only one copy class variable.

Let us use a class variable to keep the count of number of dogs born.

class Dog
@@population
end
#NameError: uninitialized class variable @@population in Dog

Above we defined a class variable population. Class variables are defined by adding a “@@”" before the variable name. But we need to initialize a class variable when it is being defined.

class Dog
@@population = 0
end

We will increment the count of population after each dog is born. We can do this in the initialize method. We will also add a class method to access the value of the class variable.

class Dog
 
 @@population = 0
 
 def initialize
  @@population += 1
 end

 def self.population
  return @@population
 end
end

puts Dog.population # 0
d1 = Dog.new
puts Dog.population # 1

d1 = Dog.new
puts Dog.population # 1

d2 = Dog.new
puts Dog.population # 2

d3 = Dog.new
puts Dog.population # 3

Note how the class method is defined. A extra “self.” is added in front of the method name.

Let us try accessing the object_id(it is unique for every object) of the variable @@population in a class method and instance method.

class Dog
 
 @@population = 0
 
 def initialize
  @@population ++
 end

 def self.population
   return @@population
 end

 def population
   return @@population
 end
 def self.class_population_id
   return @@population.object_id
 end
 
 def instance_population_id
   return @@population.object_id
 end


end

puts Dog.population  # 0
d1 = Dog.new
d2 = Dog.new
d3 = Dog.new
d4 = Dog.new
puts Dog.population  # 4
puts d1.instance_population_id # 233323
puts d2.instance_population_id # 233323
puts d3.instance_population_id # 233323
puts d4.instance_population_id # 233323
puts Dog.class_population_id #233323

Above we have done the following

  • defined a class and instance method “population” to access the population count.
  • defined a class and instance method “class_population_id” and “instance_population_id” to access the object_id of the class variable @@population.

The value of the population count from class and different instances is the same.

The object_id of class_variable @@population is also same from class and instance methods.
This implies that there is only one copy of the class variable.

More examples of a class method could be

  • a find method to find the dogs with a particular name
  • a count method which will return the total number of dogs
  • an average age method which returns the average age of all the dogs which are alive.

Note the above all methods are performing some operation on a set of dogs, not on an individual dog, so they have to be defined as a class method.

We can use instance methods in place of class method, and call it with reference to a particular instance, but it is not the correct way.

class Dog
 def average_weight
    puts "This should have been defined as a class method"
 end
end
dog = Dog.new
dog.average_weight # This should have been defined as a class method

end

class Dog
 def self.average_weight
    puts "perfect"
 end
end

Dog.average_weight #perfect

We can also use class methods in place of instance methods by passing the instance as a parameter to the method but again that is not the correct way.

class Dog
 def self.weight(dog)
      puts "this should have been as instance method as it is related only to a particular dog."
 end
end
dog = Dog.new
Dog.weight(dog) #this should have been as instance method as it is related only to a particular dog.

class Dog
 def weight
      puts "perfect"
 end
end
dog = Dog.new
dog.weight #perfect

So In this post we have seen the following
Public And Private methods
Class methods and Instance methods
Class variable and instance variable.

More in the next post.

Share

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.