I can remember interviewing for my first software development jobs just after graduating school. It seemed like almost every interview and most job listings mentioned the four pillars of object-oriented programming (OOP), also referred to as the principles of object-oriented programming.
In fact, I may have missed several potential opportunities from fumbling this question in the interview. Don’t let the same thing happen to you.
The reason interviewers ask about this, besides getting a sense of satisfaction to see you sweat through your collared shirt, is that based on how you answer this question, they can determine your level of understanding of OOP and if you understand why we do OOP in the first place.
Object-oriented Programming
In what now seems like ancient times, there was the procedural programming paradigm. Programs could be built with purely procedural code, but maintaining them was a nightmare. Let’s look at this procedural code, written in C++:
Now, compare that to this OOP code (JavaScript):
As you can see, the OOP code is much easier to understand. This is just a small code example. As projects grow, it becomes more and more important to have well organized code. Objects allow us to organize our data in ways that make it much easier for ourselves and other developers who have to maintain the code.
To get the full benefit of OOP, it is important to understand the four pillars of OOP: abstraction, inheritance, encapsulation and polymorphism.
Abstraction
The first pillar, abstraction, is when we only show what is relevant to the user of our object. We hide the implementation details.
Let me show you with an example where we calculate the perimeter of a rectangle. The user of our rectangle object sees the calculatePerimeter() method:
They do not care how the perimeter is calculated as long as it is calculated correctly. Behind the scenes, our calculatePerimeter() method could look like this:
Or it could look like this:
This is a simple example. Another example could be the thermostat in your home. You don’t want to have to understand the complexities of how it gets the air conditioning or heat going, you just want to be able to press some buttons and have the temperature change.
The benefit of abstraction is increased maintainability because it’s easier for yourself and other developers to understand how the code is working. Let’s say you’re building a house. Since the complexities are abstracted away, you could just call buildHouse() instead of having to call pourFoundation(), completeFraming(), completePlumbing(), etc.
Inheritance
In addition to having increased code maintainability from abstraction, we can achieve code reusability through inheritance. Inheritance allows us to take a class and inherit properties or methods from it so that we do not have to redefine them when we create new classes.
This is very useful in situations where we have multiple classes that are very similar and differ slightly.
Let’s say we’re creating an application that deals with plants with a Plant class being the parent class or super class:
Now we can create Dandelion, Oaktree and Spinach classes that all inherit from Plant (just going to show Dandelion for simplicity):
We can now extend our child classes (also called subclasses) by adding additional properties and methods in addition to the ones they inherit from Plant:
This is an oversimplified example, but hopefully you can see the benefit of being able to inherit properties and methods from one class to another. Otherwise, we would have to copy the photosynthesize() and reproduce() methods over to every class we made that was a plant-type. Inheritance allows our application to be much more maintainable; if sometime in the future we needed to modify the photosynthesize() or reproduce() methods, we would only have to make changes in one place, the Plant class, as opposed to having to make changes in every class that is of type Plant.
Encapsulation
Encapsulation is when we bundle together data along with the methods that operate on that data into one unit or class. We do not want our data to be able to be directly manipulated (unless explicitly allowed) by the user, but indirectly through methods that we define.
Take a look at this code example of a Car class:
Just a side note here. In a structured programming language like C# or Java, I could simply declare _fuelLevel and _mileage as private variables. JavaScript doesn’t have a built-in way to do that so I had to do a work around.
We do not want the user to be able to directly manipulate fuel level or mileage, but through the drive() and refuel() methods that indirectly operate on them.
In essence, this prevents our properties and methods from being accessed or manipulated in unintended ways. It also makes our code more flexible and easier to maintain. For example, if a public method is being accessed in many places in the code and that method changes, all of the code accessing that method could potentially break. On the other hand, if it were set to private in the first place, we would only have to make a change in one place and it would be much easier to unit test.
Polymorphism
Lastly, polymorphism is the fourth pillar. Polymorphism is when we share a common interface for multiple types, but have different implementations for different types.
A real life example could be a computer USB port. We can connect different devices to the same port, and the host computer will install the appropriate driver for each one. In other words, the same interface is used for multiple devices, but the behavior is different for each one.
Seeing this in code looks like this:
For each shape, we can call the respective methods, calculatePerimeter() and calculateArea(). The behavior will be different depending on the current shape in the iteration.
Conclusion
When trying to review these principles for my interviews, it was difficult was finding a clear explanation of each one from a single source. I had to go through multiple websites before having a comprehensive understanding of all four principles. Hopefully this post can be a nice reference for you when you need it.