Object-Oriented Programming in Ruby Explained
Tags: beginners, programming, ruby
Published: 2022-11-06 03:19:56

What is object-oriented programming(OOP)? This is a common interview question that most developers will be asked when interviewing for a developer role. OOP is essentially a style of programming that is best characterized by four core concepts which includes, encapsulation, inheritance, abstraction, and polymorphism. In this blog we will address each of the four core concepts using ruby code. 

## Encapsulation

In programming encapsulation is when we group related variables and methods that operate on them into objects. An example of this would be Ruby classes. 

```
class Dog

  def bark
    'bark bark'
  end

end
```

```
class Cat

  def meow
    'meow'
  end

end
```

In the example above we have a Dog class(an object). Now depending on how they are defined, anything defined within the Dog class can either be accessed by the Dog class itself or instances of the Dog class. Currently, the Dog class contains a method called bark that returns the string 'bark bark'. If we were to create a separate Cat class that is completely unrelated to the Dog class then that Cat class and any instances of the Cat class itself cannot access any of the variables and methods defined within Dog. In this example it makes perfect sense since a cat does not bark. The advantage of modularizing code based on related data makes it easier for developers to organize and write cleaner code. 

## Inheritance

What if there is a relationship between 2 classes? For example, lets say we have another class called Animal. Like so:

```
class Animal

  def this_is_an_animal
      'I am an animal.'
  end

end
```
 
Well a dog is an animal so it only makes sense that the Dog class have access to the this_is_an_animal method and anything else defined in the Animal class. We can establish this relationship by making Dog a subclass of Animal by using the following syntax:

```
class Dog < Animal

  def bark
    'bark bark'
  end

end
```

Using the syntax <, Dog is currently the the subclass of the superclass Animal and inherits the characteristics of Animal, in this case includes the this_is_an_animal method. Using the example of the Cat class before, Cat is unrelated to Dog but is related to Animal since it is indeed an animal. The advantage of inheritance is that it eliminates redundant code. In this case, rather than writing the this_is_an_animal method in each of the Dog and Cat class we can simply define it once in the Animal class and have Dog and Cat inherit from Animal. 

## Abstraction
In OOP abstraction pertains to the concept of only introducing relevant data about an object and hides everything else. In practice this concept can be understood through the use of private methods.

```
class Restaurant 
     
    public
  
    def server 
        "Here's your food."
        chef
    end
        
    private 
  
    def chef 
        "Food"
    end
end
  
  
# creating a customer object of class Restaurant 
customer = Restaurant.new
  
# calling the public method of class server 
customer.server
```
In the above example we've created a Restaurant class that contains a public method called server and a private method called chef. Using the concept of a Restaurant as an example, a customer interacts with the server and usually never interacts with the chef throughout his/her experience at the Restaurant. The server takes the customer's order and then interacts with the chef who provides the server with the food which is then brought to the customer by the server. 

The methods in the Restaurant class represent just that. The chef is a private method within the Restaurant class. As a private method the chef cannot be accessed directly outside the Restaurant class but can be accessed anywhere within the class, in this case by the server. Then the customer instance outside of the Restaurant class can call the server for their food. From a customer perspective there is no need to interact with the chef method since the server takes our order and brings us the food. We only expose the relevant method server outside the Restaurant class and hide the non-relevant chef method within the class.

So why go through the trouble of abstracting this code? The two main benefits is that it makes the interaction with the class simpler and helps reduce the impact of change. For our example, in terms of simplicity the customer limited his/her interaction with just the server which is simple enough. In terms of reducing the impact of change, now imagine at a restaurant that a chef not only makes the food for the customer but takes the orders as well and in this example let's say multiple customers decides to change their order. This can potentially be disastrous if the chef changes the wrong orders. Multiple tables can potentially get the wrong order out of the entire restaurant and nobody would know who ordered what. But if we had a server, we would be able to figure out the customers order quicker in the event of a mistake since each server has their own assigned tables and we would simply need to limit our search to those tables compared to the entire restaurant.  

## Polymorphism

Poly means many. Morph means form. So polymorphism means many forms. In OOP polymorphism is a technique that allows you to get rid of long if and else or switch and case statements. It does this by allowing one to execute the same method using different objects and obtain different results based on the different input objects. Below you will see polymorphism in its simplest form using inheritance.

```
Class Animal
    def message
        'This is an animal.'
    end
end
   
class Dog < Animal
    def message
        'bark bark'
    end
end
    
class Cat < Animal
    def message
        'Meow'
    end
end
  
# Creating object 
animal = Animal.new
animal.message
   
# Creating different object calling same function 
animal = Dog.new
animal.message
   
# Creating different object calling same function 
animal = Cat.new
animal.message
```
In the above example, although we are calling the same method message 3 times, the output each time will be different due to the rules of inheritance. In inheritance, if there is a method in the superclass that has the same name as the method in the current class, then the method in the current class overrides the one in the superclass by default. Since each class has their own version of the method message the output will be different depending on which object calls message. 

## Conclusion 
In summary, it is safe to say that OOP is designed to make the jobs of developers easier. Using encapsulation we group related variables and functions together and this way we reduce complexity and be able to reuse this object in different parts of a program or in different programs. With abstraction we hide the details and the complexity and show only the essentials. This technique reduces complexity and also isolates the impact of changes in the code. With inheritance we can eliminate redundant code. Finally, with polymorphism we can refactor ugly switch case statements.

## Resources
https://www.geeksforgeeks.org/ruby-encapsulation/
https://www.geeksforgeeks.org/ruby-inheritance/
https://www.geeksforgeeks.org/data-abstraction-in-ruby/
https://www.geeksforgeeks.org/polymorphism-in-ruby/