We begin with our Object-Oriented Programming Course, with this first introduction in which we will see what it is, where it comes from, and what its purpose is.
Object-Oriented Programming (OOP) is a programming paradigm that is based on the concept of “objects”, which are entities that encapsulate data and behaviors, and the relationships between them.
Object-Oriented Programming seeks:
- Modularity: Allows dividing a system into independent components that can be developed, tested, and maintained separately.
- Reusability: Facilitates code reuse through inheritance and class composition.
- Flexibility: Allows adapting software to changes in requirements or operating environments more easily.
- Scalability: Enables scaling software by adding new objects and classes as needed, without affecting the existing structure.
Without too much fear, we can say that it is the programming paradigm that has had the most influence in the last 50 years.
But before we start talking about classes, inheritance, and interfaces like crazy, let’s begin by looking at where it comes from, and why it is so important.
Basic programming knowledge is assumed. If this is not the case, I recommend you take a look at our Introduction to Programming Course
Where Object-Oriented Programming Comes From
Let’s turn back 50 years. At that time, computing was in its infancy 🐣, and neither the tools nor the needs regarding programs were what they are today.
You could say that “everything was yet to be done.” But what they were clear about at that time was that it was convenient to group data into containers (data structures).
For example, if you had a person, it was logical to have
Person {
string Name;
string Surname;
DateTime BirthDate;
}
This grouping was convenient, so we could work with a Person
collectively. Otherwise, you risked accidentally mixing one person’s data with another’s.
Moreover, these groupings could be composed. That is, one of your groupings could contain other groupings. For example, let’s imagine you wanted to create a program that drew geometric shapes.
Point2D {
int x;
int y;
}
Shape {
Point2D Position; // composition with Point2D
int Rotation;
int Width;
int Height;
}
Where Shape
uses Point2D
as its position. So far, so good. We have our data containers (very nice and very practical).
Now you could use your shape, and use it to create Squares, Rectangles, Equilateral Triangles, and Circles. You simply used your Shape
container and filled in the data as needed.
Here Comes the Problem
The problem arises when, in addition to data, behavior is to be added.
Imagine we want to add a function that calculates the area. It makes sense for a shape to know how to calculate its area because it is something inherent and internal to the shape (it does not depend on anyone else).
At that time, the natural tendency to solve this was to employ function references as members of your grouping. For example,
Shape {
Point2D Position;
int Rotation;
int Width;
int Height;
int GetArea(); // we have added a reference to a function
}
But of course, a Rectangle, a Circle, and an Equilateral Triangle do not calculate their Area with the same function.
So it doesn’t matter, I just create shapes. As I create them, I change the function (directly in memory!) according to what I need.
And then you’ve messed it up…
3000 lines and 217 files later, when you called a function, you had no idea which function you were calling, because it had been changed 14 times throughout the program.
Imagine the problem of having to create a payment platform and not being 100% clear about how each part works! (you might end up getting fired or sued, or both)
Object-Oriented Programming
Doing it this way, the situation had gotten quite tangled. There was no way to create programs well like this; it was not sustainable. So some very smart people started thinking.
They thought a lot. They visualized 12-dimensional universes. They modeled the world. They laid the foundations for a new programming paradigm.
They were not just trying to solve a problem. They were trying to invent a methodology that would allow systematically addressing the resolution of any programming problem (which is no small feat).
After much thought, they said, we got it 💡! What we need are four pillars:
- Abstraction, through objects
- Encapsulation
- Inheritance
- Polymorphism
And they were quite satisfied with themselves. We will see these four pillars in due time, what they are, and what function each of them serves.
But what is important today is to emphasize that they are not “magical laws” or absolute truths. They were what they needed to fix the mess they had created.
Object-Oriented Programming Today
A lot of time has passed since the foundations of object-oriented programming were established. In this time, many things have changed.
Currently, there is even an increasing number of detractors of object-oriented programming. They question whether it is a paradigm that is still relevant today or has become outdated. Well, several things are happening here.
On one hand, the current needs are not the same as when those very smart people sat down to think. Programming is now much more complex and encompasses needs that were not even envisioned at that time.
On the other hand, object-oriented programming has also changed. In fact, it has become more complex and rigid. Even, in the words of some of its creators, it has evolved and deviated from its original purpose.
That does not mean that object-oriented programming is bad or outdated. On the contrary, we have said that it is a paradigm that comes naturally, as you create increasingly complex programs.
What is necessary is to understand the foundations and purpose of each of them. It is not a problem of the paradigm, but of its use. You need to know how to apply it and adapt it to modern times.
That is what we will focus on in this course, where we will see all these things. Where each of them comes from. And the practices that make sense to apply today, and those that are best avoided.