Classes and Objects in Ruby
Ruby is an object oriented programming language.
Now for the uninitiated let’s start with the beginning. To understand the concept of a class let’s take an example of a real world physical entity, a dog, which will represent a class. There can be many dogs with same behaviour(or states). Now, you can take one dog in particular, let’s say tommy
tommy = Dog.new(“tommy”)
Now tommy is an instance or an object of a the class Dog, because it has all the behaviours of that class. A dog has 5 possible behaviours: eat, sleep, walk, bite & woof. It also has functionality that allows you to change its state, you can feed it, for it to be eating.
Object-oriented programming (OOP) is a programming paradigm that allows you to manipulate an object via methods. You can change the state of an object by performing certain actions on it. You can call those actions, a method. There are few core concepts of OOPS programming:
Encapsulation
Let’s say to communicate with tommy we have 5 commands or in Ruby terms, methods, now tommy can eat(), sleep(), walk(), bite() & speak(). Now, if there is one more dog, Sophie, she can also have access to all those 5 methods independent of Tommy. Sophie and Tommy can exhibit different behaviour at any given point in time.
Encapsulation is achieved when each object keeps its state private, inside a class. Other objects don’t have direct access to this state. Instead, they can only call a list of public functions — called methods.
So, the object manages its own state via methods — and no other class can touch it unless explicitly allowed. If you want to communicate with the object, you should use the methods provided. But (by default), you can’t change the state. So if we have another class called Cat, it won’t have access to Tommy, as in Tommy can’t “meow” because tommy is an instance of class Dog.
In general encapsulation is achieved via access control. These three methods are used in Ruby for access control: private, protected and public.
Inheritance
Inheritance is the ability of related classes to share behaviours through a hierarchical structure of single inheritance. Subclasses inherit the methods from their parent classes. It’s called single inheritance because a given class can only ever directly subclass from one parent class. In our example, let’s add one more class called Animal
which would be our superclass. By subclassing the cat
and Dog
classes from this shared ancestor Animal
, both of those subclasses will inherit the behaviours and attribute of the Animal
class .
class Animal
def eat(food = "grass")
puts "I eat #{food}"
end def walk
puts "I am walking"
end
endclass Dog < Animal
attr_accessor :name def initialize(name = "doggo")
@name = name
end def eat(food = "kibbles")
super
end def speak
"woof"
end
endclass Cat < Animal
attr_accessor :name def initialize(name = "canto")
@name = name
end def eat(food = "fishy")
super
end def speak
"meow"
end
endtommy = Dog.new("Tommy")
# this line is initialising a new object of class dog, note the object will have properties of both Dog and Animal class, since Dog is inheriting Animal.tommy.walk # this will return "I am walking"
# this will change the state of object tommy as walking. Also, walk is the superclass method, all the Animals can walk, since Dog is inheriting from Animals it can also walk.tommy.eat
# this will change the state of object tommy as eating. Notice we hadn't passed any argument to method eat because we have set the default argument as "kibbles". Tommy.eat("chimken")
# now tommy is eating chimken, yomyom, okay so now we have over ride the default argument, hence tommy now gets to eat chimken. Also the method is using a keyword called super.kitty = Cat.new("kitty")
# this line is initialising a new object of class Cat, note the object will have properties of both Cat and Animal class, since Cat is inheriting Animal.Note: name would be the attribute of class Dog. It would be unique for each object. There are two concepts here, attribute and behaviour, attributes would be instance_variables because they are uniq to each object and behaviours would be your methods.
Polymorphism
Polymorphism is a made up of two words Poly which means Many and Morph which means Forms. In Ruby, it means being able to send the same message to different objects and get different results. Let’s look at a few different ways to achieve this.
Polymorphism using Inheritance
From our previous example, if we have defined a method called eat
in our parent class Animal
, which we are again using in our child class Dog
. Note the use of keyword super in our child class. The use of keyword super denotes that when a method with arguments is overridden by one of its child classes then a call to super
without any argument in the child method will automatically pass the arguments of the child method to the parent method.
Polymorphism using Duck-Typing
Duck Typing is a funny notion. As James Whitcomb Riley said it :
When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.
Rather than having to check that an object is of a specific type, all we are concerned about is that the object can answer to a method call in a specific context. So in our previous example tommy would woof
if we called speak
and kitty would meow.
Abstraction
Abstraction can be thought of as a natural extension of encapsulation. Applying abstraction means that each object should only expose a high-level mechanism for using it. This mechanism should hide internal implementation details. It should only reveal operations relevant for the other objects. Preferably, this mechanism should be easy to use and should rarely change over time. Think of it as a small set of public methods which any other class can call without “knowing” how they work.
UNDERSTANDING INSTANCE & CLASS METHODS
In programming world it’s often how we think about a particular problem that causes it not to work as we expected! For example, a method can never be an instance or a class, instead a method is accessible via instance or class. Let’s take this instance :
class Example
def self.class_method
instance_method # It will throw NoMethodError
end def instance_method
# some data
end
end
So, why did it throw an error, there is definitely a method called instance_method. What’s happening here? Actually ruby is searching for a class method, because ruby will read it as self.instance_method
Here self is the “class” itself, hence the error. Keep in mind that Class methods do not have access to instance methods or instance variables. However instance methods can access both class methods and class variables.