DEV Community

Cover image for The Four Pillars of Object Oriented Programming
Todd Carlson
Todd Carlson

Posted on

The Four Pillars of Object Oriented Programming

Whether you love, or hate, object oriented programming, "What are the four pillars of OOP?" will be a question you get asked on interviews again and again. I know this because a good friend of mine, who is a newly minted CS grad, messaged me upset because she completely blanked on this question during one of her interviews, and I was literally asked this question yesterday during a technical interview.

Just a quick disclaimer, this post is not an exhaustive and deep dive into the four pillars. I am merely providing quick "in a nut shell" explanations of the four pillars, with the sole aim of helping people answer this question when it inevitably comes up during an interview.

So what are the four pillars of OOP? They are Inheritance, Encapsulation, Polymorphism, and Abstraction. I will start with Encapsulation, which I believe is the real heart of OOP.
Basically, Encapsulation is keeping your state and behavior in the same place. I also like to think of it as a public contract with private implementation. For example, say you have a class called Customer:

class Customer
   def initialize(id, name, addr)
      @cust_id = id
      @cust_name = name
      @cust_addr = addr
   end
   def display_details
      puts "Customer id #@cust_id"
      puts "Customer name #@cust_name"
      puts "Customer address #@cust_addr"
   end
end
Enter fullscreen mode Exit fullscreen mode

So, we have this class called Customer, with some instance variables, and a method that displays those instance variables. Those instance variables describe the state of our class, while the method describes the behavior. They are both "encapsulated" inside of our class. The public contract aspect of this that I mentioned earlier, means that this class is guaranteed to have this state and behavior. However, the private implementation aspect that I mentioned comes into play with how I choose to use the class. I could write new methods that display only one of the instance variables, or I could write new methods that add additional behavior. However, the main concept of Encapsulation is that state and behavior are in the same place.

Inheritance is a relation between two classes. We know that all dogs are animals. The benefit of inheritance is that classes lower down the hierarchy get the features of those higher up, but can also add specific features of their own. If all animals breathe, then all dogs breathe. In Ruby, a class can only inherit from a single other class. Some other languages support multiple inheritance, a feature that allows classes to inherit features from multiple classes, but Ruby doesn't support this.

class Animal  
  def breathe  
    puts "inhale and exhale"  
  end  
end  

class Dog < Animal  
  def speak  
    puts "Woof!"  
  end  
end  

doug = Dog.new  
doug.breathe  
doug.speak  
Enter fullscreen mode Exit fullscreen mode

Polymorphism comes form the greek words "polys" meaning much or many and "morphe" meaning form or shape. In programming this means that you can have methods that all share the same name, but have different implementations. For example, say you have this class called Person:

class Person
    def initialize(first, last, age)
         @first_name = first
         @last_name = last
         @age = age
    end

    def birthday
         @age += 1
    end

    def introduce
          puts "Hi everyone. My name is #{@first_name} #{@last_name}."
    end
end
Enter fullscreen mode Exit fullscreen mode

This is all well and good, but let's also say that you want to get more specific with your person, say you also have a class of Teacher, and a class of Student. We want to keep the state and behavior of our Person class, so we will have our Teacher and Student classes inherit from the Person class. The beauty of Polymorphism is that we can keep the original "introduce" method that we get from our person class, and give it new behavior for each of the other two classes like this:

class Person
    def initialize(first, last, age)
         @first_name = first
         @last_name = last
         @age = age
    end

    def birthday
         @age += 1
    end

    def introduce
          puts "Hi everyone. My name is #{@first_name} #{@last_name}."
    end
end

class Student < Person
    def introduce
          puts "Hello teacher. My name is #{@first_name} #{@last_name}."
    end
end

class Teacher < Person
    def introduce
          puts "Hello class. My name is #{@first_name} #{@last_name}."
    end
end
Enter fullscreen mode Exit fullscreen mode

The Teacher and Student classes both have a method called "introduce," but it behaves differently, or it has a different implementation thanks to polymorphism. We have overridden the original method behavior, but we have kept the same name.

Lastly we have abstraction, which is basically the idea of only showing essential attributes to the user, and hiding any unnecessary details/information. Abstraction is selecting data from a larger pool, and only showing the relevant details of the object to the user. It helps reduce programming complexity. An example that helps me wrap my head around the concept is fetching data from an API. Say you make a fetch request that gives you back 100 blog posts in the form of an array of objects. Each object contains the properties of, title, body, author, publication date, publisher etc. For the purposes of your website, you want to display the first four posts on your page, and only include the title and body. So you write some JavaScript and do some DOM manipulation that allows you to do just that. In this case you have abstracted away all of the posts that you don't want to display, and all of the other object properties that you don't need as well. You still get all of that information when you make your initial fetch request, but you have hidden, or abstracted it away from your user.

I hope this post has helped someone understand the basics of the four pillars of OOP. Other OOP languages may do things a little differently than the examples I have shown in Ruby, for example you can't overload in Ruby because it isn't a strongly typed language, but the rest of the concepts are pretty much the same across the board.

Top comments (6)

Collapse
 
aleron75 profile image
Alessandro Ronchi

Regarding OOP, I warmly suggest reading this piece from Alan Kay, one of the fathers of OOP, that dates back to 1998: wiki.c2.com/?AlanKayOnMessaging

TL;DR: the big idea [of OOP] is "messaging"

Collapse
 
toddster79 profile image
Todd Carlson

Thanks for the article, I will check it out for sure!

Collapse
 
jonathanhiggs profile image
Jonathan Higgs

I think composition is far more important than inheritance and polymorphism, and the lack of attention it recieves is often to the detriment of OOP as a whole. Take the animal inheritance as an example, in almost every case except trivial examples the use of composition reduces code complexity. e.g. similar traits or features evolved in distinct evolutionaly trees, so you would have to repeat the implementation in multiple places. Continuing the evolutionary example, there are cases of traits evolving and then being lost which also wouldn't work with an inheritance style of programming

Collapse
 
toddster79 profile image
Todd Carlson

I 100% agree! My favorite instructor said that when it comes to inheritance, you almost always get the hierarchy wrong. He was a champion of composition too!

Collapse
 
hsorellana profile image
Hugo Soto

In the example of inheritance, shouldn't dog inherit from Animal instead of Mammal??

Collapse
 
toddster79 profile image
Todd Carlson

Yes, it should! Thank you for bringing that to my attention. I have fixed the mistake. I couldn't initially decide between mammal and animal. Naming things is the hardest part of programming right ; )