What is OOP (Object-oriented Programming)?
OOP is a programming paradigm that believes in grouping data (properties) and methods (actions) together inside a box. It demonstrates the pattern of real-world objects.
There are two types of OOP languages:
1. Class-Based languages like JAVA, C++.
But what makes a language an OOP-based programming language?
There are four primary pillars of Object-Oriented Programming. Any piece of code following the OOP paradigm must adhere to these basic rules of Object-orientation.
Features of an Object oriented language
Object-Oriented Programming (OOP) The object-oriented paradigm keeps data and actions grouped together inside classes. In OOP, we create classes and then create their instances called objects.
Prototype-based Programming In Prototype-based programming, we derive objects from other already existing objects.
As already explained, an object is a data structure containing properties and methods as key-value pairs in it. But how do we create one?
Let's see. There are three ways to create one. We'll be discussing each one of them in detail.
Using an Object Literal
We can simply create an object, by putting in the key value pairs for the object. The data variables can simply have a number, a string, an array or an object as its value. A data function has a functions as its value.
Let us take a look at an example:
This way of creating an object is hard-coded. What if we want to create objects for 100 users having the same three data members - first_name, last_name, and display_full_name? We'll have to write the whole object 100 times. So, what's better?
Note: this keyword is used to refer to the current object. To read more about this keyword, read this article for in-depth explanation.
Using an Object Constructor
We can create an object prototype and then create 100 instances for the same. Using Object Constructor is one way of doing this.
Using Object.create() Method
This is another method for creating objects. Simply create an object and create instances using the Object.create() method.
Let's see an example:
Let's look at the ES6 way of defining classes, using the class keyword and then we'll see how classes were defined traditionally.
This way, we create a class using the class keyword. Define constructor function and all the other data functions inside the class declaration function. Then we create instances for this class using the new keyword.
Note: The new keyword binds the this keyword to the object. Hence, we should always create instances using the new keyword.
Traditional Way of Class Simulation
Encapsulation puts the data variables and the data functions together inside a box. Encapsulation ensures that data can only be accessed using the data functions defined inside the class, and abstraction ensures not anyone outside this encapsulated box can access it.
Let's discuss both of them with examples.
When we define a variable or a function inside the function, we can only access it from inside and not from outside the function. This can be one way of implementing abstraction. Look at the example below.
The output for this code snippet says the right message when accessed from inside but throws an error when accessed from outside. Hence, the message is encapsulated.
We can create a function inside a function, and the inner function will be able to access the local variable defined inside the outer function. This is called closure.
Note: A closure is created every time a function is declared.
Again, the output says that creating a closure helps encapsulate and restrict the data access only to inside the function.
Let's create an object and console it.
We only defined the property message to the messageObject, but we can see a lot of other methods. We didn't define them!
These methods are inherited from the parent object, i.e., the prototype for the object data structure. When we create the object messageObject, we basically inherit all the properties object data structure has and add a new one, i.e. message.
What is Polymorphism?
Generics (Parametric Polymorphism)
Parametric Polymorphism is implemented to make our code more generic. More precisely, it makes our code independent of defining data types. A more generic code doesn't care about the types while still maintaining types-safety.
Let us see an example:
Let's create a function to concat two data structures. Now, applying parametric Polymorphism, this code should run for two lists, two strings, or any two types. This is the idea behind this type of Polymorphism.
This code runs for both arrays and strings. Hence, Polymorphism is implemented.
Overloading (ad-hoc polymorphism)
- + when used on integers and floats, exhibit the property of addition.
- + when used with strings and array does concatenation.
Structural Subtyping (Structural Polymorphism)
Consider Person and Student.
Clearly, Student is a sub-type of Person, and Person is a super-type of Student. All the operations that can be performed on the Person can also be applied to the Student, but the reverse is not valid. Structural subtyping is about defining relationships. A sub-type will always have all the properties of another type, plus some additional features.
Let's elaborate on these three principles:
When two objects are unrelated and independent of each other, we define a relationship between them independent of any hierarchy. This means none of the objects is the child or parent of the other object, and the relation is called an association.
For example: In the case of a patient visiting a doctor, there is an independent relation. That doctor might be seeing multiple patients, and that patient can also be visiting multiple doctors simultaneously. None of them is a parent or a child. Hence, the relation is built through association.
There is an ownership in this type of relationship. It holds a parent/child relation. An essential property of this type of relationship is that the parent can live without the child object and vice versa. The child can also exist independently.
For example If an employee works for more than one department in a company, and anyone department gets deleted, the child, i.e., the employee, can still exist inside the company. Hence, this relationship exhibits aggregation.
Composition is a special type of aggregation. If the parent object dies, the child object also ceases to exist. To be more precise, the parent can exist without the child object, but vice versa is not true. The child cannot exist without its parent object.
- Classes are blueprints for real objects, and objects are the instances of the classes/prototypes.
- Encapsulation is binding data and actions together, and inheritance says we can inherit some properties from the parent objects and define new properties and actions inside the child object.