1. Introducing the principles of OOP
Today you will discover a new and interesting world. The world of Object-Oriented Programming (OOP). You previously learned about classes and objects. Today you will learn more about them, much more.
OOP emerged as a response to the increasing complexity of programs. When variables and methods in programs began to number the tens of thousands, it became clear that something had to be done. One solution was to combine data and associated methods into separate objects.
Now programmers had to separately describe how objects interact externally and how they behave internally. This made it much easier to understand and write programs. However, a question remained: which methods should be internal to an object and which should be available to other objects?
Many approaches were attempted. And based on the discovered best practices, 4 principles of OOP emerged. They are abstraction, encapsulation, inheritance and polymorphism. Previously, there were only three, but experts later decided to add abstraction as well.
People on the Internet still argue over the definition of abstraction in OOP. The problem isn't that everyone is wrong. It is that everyone is right. The smaller a program is, the more abstraction is tied to the Java language. The bigger a program is, the more abstraction is tied to modeling/simplifying real-world objects.
But the best minds seem to agree that:
Abstraction is the use of only those characteristics of an object that represent it sufficiently accurately in a program. The main idea is to represent the object with the smallest set of fields and methods that simultaneously allow your tasks to be solved with sufficient accuracy.
In Java, abstraction is accomplished through abstract classes and interfaces.
Abstraction in real life
A good example of abstraction in real life is job descriptions at a company or organization. The title of a position is one thing, but the specific responsibilities assigned to a position are a completely different matter.
Imagine that you're designing the organizational structure of your future company. You can divide secretarial responsibilities, scattering them across several different positions. You can split the CEO position into several separate positions: CFO, CTO, CMO, HR Director. Or, for example, you can combine the positions of office manager and recruiter into one.
You think up job titles and then divvy up responsibilities for these positions. Abstraction is breaking away from the object as a whole and selecting the essential properties and components that we need.
From a programming point of view, abstraction is the correct division of a program into objects. Typically, there are dozens of ways to represent any large program as interacting objects. Abstraction lets you select the essential characteristics and ignore the non-essential ones.
The goal of encapsulation is to improve interactions by making objects simpler.
And the best way to simplify something is to hide anything complicated from prying eyes. For example, if you get put into the cockpit of a Boeing jumbo jet, you won't immediately understand how to operate it:
But for the passengers on the plane, everything is simpler: you buy a ticket and get on the plane, which takes off and then lands. You can easily fly from one continent to another with only the ability to "buy a ticket" and "get on a plane". All the complexity of preparing the aircraft for flight, takeoff, landing and various emergency situations is hidden from you. Not to mention the satellite navigation, autopilot and air traffic control centers. And this simplifies life for us.
From a programming point of view, encapsulation is "hiding the implementation". I like this definition. Our class can contain hundreds of methods and implement very complex behaviors in various situations. But we can hide all its methods from prying eyes (by marking them with the private modifier), and leave only two or three methods to interact with other classes (by marking them with the public modifier). Then all other classes of our program will only see the three public methods and call them but not others. And all the complexity will be hidden inside the class, just like the cockpit is hidden from happy passengers.
There are two sides to inheritance. Inheritance in programming and inheritance in real life. In programming, inheritance is a special relationship between two classes. But inheritance in real life is far more interesting.
If we need to create something in real life, we have two options:
- Create the thing we need from scratch, spending a lot of time and effort.
- Create the thing we need based on something that already exists.
The optimal strategy is this: take an existing good solution, modify it a little, adjust it to fit our needs, and then use it.
If we trace the human history back to its beginnings, we discover that billions of years have passed since life began on the planet. But if we consider the starting point of humans to be our primate ancestors (i.e. humans are "based on" primate ancestors), then only a couple of million years have passed. Building from scratch takes longer. Much longer.
In programming, you can do something similar by creating one class based on another. The new class descends from (inherits) the existing class. This is very beneficial when an existing class has 80-90% of the data and methods we need. We simply declare the appropriate class as the parent of our new class, and all the data and methods of the parent class automatically appear in the new class. Convenient, right?
Polymorphism is a programming concept. It describes a situation where different implementations are hidden behind a single interface. Looking to real life for a counterpart, we find that operating a vehicle is a good fit.
If someone can drive a truck, then she can also be put behind the wheel of an ambulance or behind the wheel of a sports car. A person can operate a car, regardless of the kind of car, because all cars have the same control interface: a steering wheel, pedals and gearshift. The cars' internals are different, but they all have the same control interface.
Returning to the world of programming, polymorphism allows objects of different classes (usually having a common ancestor) to be accessed in the same way, a property whose value can hardly be overstated. Its value increases as the program grows larger.
OOP means principles. Internal laws. Each of them limits us in some way, but provides great benefits in return when the program becomes large. The four principles of OOP are like four legs of a table. Take one away and the whole system becomes unstable.