When objected-oriented programming first appeared in the 1980s, it was a radical leap from traditional top-down programming. OOPS introduced new software development methods with features like Inheritance, Encapsulation, Abstraction, Polymorphism, etc. Simply put, it helped us organize code and thinking for large-scale software development.
OOPS binds together data and methods in the form of an object and selectively exposes data to other objects. The idea revolves around classes and objects: Design, instantiation, relationship and communication, etc. On the other hand, the building block of procedural programming is procedures or methods that perform operations on data.
Procedural vs Object Oriented Programming
Key features of object oriented programming
Class: A blueprint of a real-life structure
Why is the idea of class essential in OOPS? Here is an analogy: The world around us has a much more complex structure than just numbers, strings, etc. So we need a mechanism to define and use such a complex structure in a meaningful fashion.
In other words, If we want to solve real-world problems using software, we should mimic the real-world structures with attributes and behaviours that exhibit the characteristics of that structure. So classes in oops help us define structures similar to the real world and build scalable, reusable, modular and extensible software.
OOPS allows us to combine methods and data into a specific group called classes. This idea brings data and methods together in a code and, most importantly, makes the code reusable. It allows us to represent real-world entities using their attributes and behaviours.
Object: An Instance of a Class
An object is an instance of a class created with specific data, whereas a class is an abstract blueprint used to create specific or concrete objects. So each object contains data and methods working on that data.
This approach allows us to access class variables to the class methods while, at the same time, they aren’t global. It gives us a lot of control over which parts of the program can access what, which could help improve code clarity.
- When a class is defined, no memory is allocated.
- When an object is created, memory is allocated.
- Objects interact by sending messages to one another.
- Objects can also interact without knowing each other details.
Encapsulation: Hiding implementation details
When working with object-oriented programming languages, we know exactly where to look when something goes wrong. In other words, we should have to go line-by-line through all our code.
Encapsulation helps us hide implementation details, expose relevant details of a class to the outside world, and lets us change the implementation of the code without affecting the code of any other class.
Objects are self-contained, and each functionality does its own thing while leaving the other alone. This modularity allows an IT team to work on multiple objects simultaneously while minimising the chance that one person might duplicate someone else’s functionality.
In other words, we often need to take care of the accessibility of our data in software projects. To achieve this, we use the concept of encapsulation!
- Encapsulation hides the implementation details of an object from the users and protects an object's data. Technically, we can hide class data from other classes and allow other classes to access that data through its member methods (most of the time using get and set methods).
- Encapsulation combines data and methods to make our code cleaner and easy to read. In simple words, it provides an abstraction between an object and the users of the object.
- Encapsulation can be achieved by declaring all the class variables as private and writing public methods in the class to set and get the values of variables.
Inheritance: Reusing code and extending functionalities
On large-scale software projects that involve millions of code lines, we often need to reuse existing code or extend functionalities according to requirements. To achieve this, we use the concept of Inheritance. It is a process in which one class acquires all the properties and behaviours of its parent class.
In Inheritance, we create one parent class and define subclasses to inherit the parent class traits. Each subclass could implement separate methods for itself, and at the same time, it can reuse the parent class's existing methods. Even if we change our parent class, all sub-classes will inherit the new code.
Polymorphism: Performing a single action in different ways
OOPS allows us to have many methods, all with the same name, doing a similar job on different data or data types. Suppose we need a few methods with similar traits in each subclass, but the parameters are different. This is where polymorphism comes into play.
We define a single method inside the parent class, and based on need, we separately define this common method for all other sub-classes.
- Polymorphism presents the common interface to perform a single action in different ways. It is crucial for code reusability and extensibility.
- Instead of complex conditional statements describing the different courses of action, we can create interchangeable objects that we select based on our needs. So one benefit of using polymorphism with inheritance is: Code working with the different classes does not need to know which class it uses!
Polymorphism via Function Overloading
Function overloading allows us to have more than one function having the same name but a different parameter list or type. For example, the parameters list of a function myfun(int a, int b) is (int, float) which is different from the function myfun(float a, int b) parameter list (float, int).
The main advantage of function overloading is to improve code readability and allow code reusability. In the code below, we can see how we are able to have more than one function for the same task(addition) with different parameters. This allows us to add two integer numbers and three integer numbers, and if we want, we could have some more functions with the same name and four or five arguments.
Polymorphism via Operator Overloading
Operator overloading means that the operation performed by the operator depends on the type of operands provided to the operator method. It is similar to function overloading, where we have many versions of the same function differentiated by their parameter lists.
OOPS lets us extend operator overloading to user-defined types (classes) i.e., a programmer can provide their own operator to a class by overloading the built-in operator and perform some specific computation when the operator is used on objects of that class.
Abstraction: Exposing essential details
In an object-oriented language, abstraction is implemented through classes that define properties of an object like data and methods. It has the mechanism of using class methods through related objects without going into details about how the methods are written. So abstraction means providing only the essential or relevant information and hiding the background or technical implementation details. For example: to drive a car, one only needs to know the driving process and not the mechanics of the car engine.
Abstraction makes the modifications easier as the interface to access the method remains the same. For example, if the type and working of the car engine change, it does not change the driving process.
Advantages of OOPS
- Makes code easy to maintain and modify.
- Helps us write reusable code.
- Provide data security and control data accessibility.
- Provide a mechanism to support high code cohesion.
- Provide a mechanism to support minimising code coupling or dependency.
- Improve code readability and ease in the documentation.
- Simplify large-scale software development.
Additional blogs to explore
Enjoy learning, Enjoy OOPS!