Time to add another post under Programming Paradigms and here, let’s talk about Object Oriented Programming Paradigm. But before that, optionally, take a look at Imperative Programming Paradigm for more context.
What is Object Oriented Programming?
One of the most commonly used paradigm out there today is the Object Oriented Programming Paradigm (OOPP). An object is something bundles data and procedures within itself. Objects are the protagonist in this paradigm. The program is revolves around objects where the programmer writes code to create objects and tell them how to communicate with each other. This interaction between objects changes the state of the program eventually leading to the end result.
How does Object Oriented Programming work?
In OOPP, objects are modeled based on real-world entities. Just like how real-world entities interact with each other, the objects in an application communicates between each other. By interacting, real-world entities reach the final state and similarly, by communicating, the objects change the state of the application, eventually achieving the final result.
Modification of program state by the interactions and communications between objects to get the end result is the OOP style of programming.
Class-based and Prototype-based OOPP
In OOPP, objects are the protagonists and there are two ways to create objects. This is based on the style of programming dividing OOPP into two sub-paradigms.
- Class-based programming
- Prototype-based programming
If you have worked with Java, then you must be familiar with classes. Similarly, if you have worked with JavaScript, then you must be familiar with prototypes.
Class-based programming
In class-based programming, classes defines the structure of objects, i.e., it defines what variables and procedures should an object hold. An object created for a class will have it’s own identity and becomes an instance of the class. I’ll be talking more about class in under building blocks of OOPP.
Prototype-based programming
Prototype-based programming works by cloning and extending existing objects and there is no concept of class. Inheritance is by reusing existing objects that serves as prototypes.
Now, let’s dive deeper into OOPP. For this post, I’ll explain OOPP with examples based on class-based programming using Java since I have future plans for prototype-based programming.
Building Blocks of Object Oriented Programming
There are 4 building blocks for OOPP.
- Class
- Object
- Attribute
- Procedure
Class
As discussed earlier, a class is a blueprint that defines the structure of an object. By structure, I mean details like what data should the object hold and what procedures it can perform.
Let’s design a class for a real-world object. The below is the code to design a class for Car
.
class Car { String model; String manufacturer; String color; boolean isDoorOpen; void openDoor() { this.isDoorOpen = true; } void closeDoor() { this.isDoorOpen = false; } void drive(String destination) { // do some driving } }
Here, Car
is a blueprint that defines the structure of a car object. It defines what attributes and procedures that the object should have. This makes the code more reusable since I can create n
number of objects for this blueprint. Reusability is one of the virtues of OOPP. Note that while a real-world entity can have any number of attributes and can perform any number of actions, we do not have to model classes with all the attributes and actions. It is a good practice to design objects with data and procedures that are just enough for the application.
Object
In general, an object is a variable in the memory. In addition to that, in OOPP, an object contains a set of attributes (variables) and procedures (methods). The structure of an object is based on the class in class-based programming. The beauty is, the object can use it’s procedures to modify it’s attributes. Such modifications changes it’s state, eventually, leading to the end result. This makes OOPP stateful.
Usually in OOPP, objects are used to model real world elements. In a different perspective, we can say that our world is one giant application where everything in it are objects, including us. If we are objects, data like our name, age, gender, address, college degree becomes our attributes and our actions like walk, talk, run becomes our procedures. In a computer program, we can model objects based anything in the real-world.
Car carOne = new Car(); // created new object carOne // Setting the state of the object. carOne.model = "Carrera GT"; carOne.manufacturer = "Porsche"; carOne.color = "Guards Red"; carOne.numberOfSeats = 2;
In the above example, I created an object carOne
for our class Car
and set values to the attributes of the object. This sets up a state for the object. Now we know details of the car through these attributes. With the class Car
as a blueprint, any number of objects with it’s own version of attributes and procedures can be created.
Note that we are using the dot operator on the variable to access it’s attributes. An object’s attributes and procedures can be accessed only by using the dot operator.
The dot operator, also known as separator or period used to separate a variable or method from a reference variable.
What is an instance?
The English dictionary says, an instance is single occurrence of something. In programming, an instance is a single occurrence of a class. Technically, an instance is an object. While objects refer to all the objects in general, an instance refers to a specific representation of an object. Like our class Car
, we can create several other classes like Plane, Boat, Bike, etc. Each of these classes can have many number of objects. Regardless of the class, all the objects are termed as “objects”. But from our previous example, carOne
is an object but an instance of Car
.
In the previous example, I created the object carOne
using the new
keyword and assigned the newly created object to the variable
by using carOne
=
operator. By using new
keyword, we create a new “instance” of the class Car
, allocating memory to the instance. Memory to an instance is allocated only when it is created. This process is called instantiating a class. Based on the variables bundled inside, the memory size of an instance varies.
Each instance is unique and has it’s own place in the memory having it’s own version of attributes and procedures.
Car carOne = new Car(); carOne.model = "Carrera GT"; Car carTwo = new Car(); carOne.model = "Gallardo"; Car carThree = new Car(); carOne.model = "Mustang";
In the above example, we create 3 instances, carOne
, carTwo
and carThree
, for the class Car
. Each object will have it’s own version of the attribute model
.
The above image is a rough representation of how these objects will be in the memory. When an instance is created, the variables inside the instance are also instantiated and allocated memory. When I access the variable model
bundled inside carOne
with the statement carOne.model
, the program will navigate through the addresses to get the actual value. For the statement carOne.model
, the value at address 300
is accessed since the model
bundled inside carOne
points to 300
but it cannot access 301
since it belongs to carTwo
.
The keyword “this”
An object to refers to itself by using this
keyword.
class Car { private String color; public void setColor(String color) { color = color; // The variable color is in the procedure's scope. This doesn't work. this.color = color; // 'this.color' is the instance level variable and 'color' is in method's scope } }
In the above example, I’m trying to assign the value to the attribute color
in the procedure setColor
. Note that there are two variables with same name color in this procedure. Since we have two variables with same name, the first statement color = color
will not work. The operands in both the sides are same variable, we are just assigning the value of color
to itself. This is because, the scope of the variable color
in this line is within this method and it will not exist outside this method. To assign the value to the instance level attribute, we should use the statement in the second line. In the statement this.color = color
, we are referring to the object’s attribute color
by using this
keyword. The operand of the left is the instance level attribute while the operand on the right is the method level attribute.
Ok, now that’s enough about objects. Let’s move forward.
Attribute
Attributes are variables inside an object to hold information for the object. The state of an object is defined by it’s attributes. Modifying the attributes leads to state transition of the object.
Car carOne = new Car(); // created new object carOne // Setting the state of the object. carOne.model = "Carrera GT"; carOne.color = "Guards Red";
In the above example, attributes of carOne
are model
and color
. They hold information about the object like what is the model of the car and color of the car.
Procedure
A procedure, is a series of steps that can compute on the given data. They contain step-by-step instructions on how the computation should be done. Note the word “how“. That’s because Procedural Paradigm is a subset of Imperative Programming Paradigm.
Huh.. wait what? Weren’t we talking about OOPP? Where did Procedural Paradigm came from?
😅 yeah.. Procedures are part of Procedural Programming which is a sub-paradigm of Imperative Paradigm like Object Oriented Programming. Both Procedural and OOPP are siblings under Imperative Paradigm.
In Procedural Paradigm, there are no objects and the procedures existence is independent/standalone. They compute on the variables passed to them and return the result. Think of functions in functional programming.
An example
function add(var1, var2) { return var1 + var2; } add(1, 2) // returns 3
In the above example, the procedure add
is independent. It computes on the input variables to return the result.
But in OOPP, the procedures and the variables are bundled inside an object. The procedures in OOPP are,
- Tied to the object, hence they are not independent anymore.
- Can compute only on the variables of the object.
- Only the object can invoke the procedure.
class Counter { int total; public void increment() { this.total++; } } public class Oopp { public static void main(String[] args) { Counter counter = new Counter(); counter.increment(); // increments count to 1 counter.increment(); // increments count to 2 System.out.println("Count: " + counter.total); // prints 2 } }
In the above example program, I have a class Counter
and I have created an object counter
for it in the main method. Now note the below points.
- The class
Counter
has a procedureincrement
and an attributetotal
. - The procedure
increment
can only be invoked by the objectcounter
. - The object
uses the procedurecounter
increment
to perform computation on it’s own attribute
. This changes the state of the objecttotal
counter
.
So, in OOPP, only an object can operate it’s procedures on itself, changing it’s state or the state of the program.
Concepts of Object Oriented Programming
The below is a list of basic concepts in OOPP. Out of the 6 below, we already covered class and object above. Let’s see the remaining 4.
- Class
- Object
- Inheritance
- Encapsulation
- Abstraction
- Polymorphism
Inheritance
Inheritance in OOPP promotes the reusability of the code in the application. Just like it’s English meaning, inheritance in programming let’s a class to inherit data and procedures from it’s parent classes. The idea is we can create a generic parent class to have attributes or procedures that are common to all it’s child classes. These child classes can inherit such common data and procedures from the parent class rather than having duplicate codes thus promoting reusability.
Let’s consider the living things. I want to create classes for Humans and Dogs.
class Human { String name; String gender; int age; String collegeDegree; public void walk() {} public void run() {} public void readBook() {} } class Dog { String name; String gender; int age; String breed; public void walk() {} public void run() {} public void wagTail() {} }
In the above classes, most of the data and procedures are duplicated for both the classes. What if we can abstract the common items into a separate class and extend Human
and Dog
from the new class? This is what inheritance does. Let’s apply inheritance to the above code.
class Animal { String name; String gender; int age; public void walk() {} public void run() {} } class Human extends Animal { String collegeDegree; public void readBook() {} } class Dog extends Animal { String breed; public void wagTail() {} }
I have created a new class Animal
since dogs and humans are animals. I also moved the common pieces of code into the class Animal
and extended Human
and Dog
from it. The extends
keyword denotes that the new class we create is an extension of the existing class. Here, Human extends Animal
says that Human
is an extension of Animal
which is a true statement in the real-world. If I want to create a class for another animal say Cat, I don’t have to duplicate data and procedures. I can simply extend the class Animal to inherit the data and procedures that are common. This is how inheritance promotes reusability.
Encapsulation
Encapsulation is binding the data and the procedures into a single object and add an restriction on what data should be exposed to the outside of the object. We saw how an object bundles the data and procedures inside itself. By applying encapsulation, we can prevent other objects accessing the data of an object directly.
Let’s leverage the our Counter
example.
class Counter { int total; public void increment() { this.total++; } } public class Oopp { public static void main(String[] args) { Counter counter = new Counter(); counter.increment(); // increments count to 1 counter.increment(); // increments count to 2 System.out.println("Count: " + counter.total); // prints 2 } }
In the above code, see how we can access total
directly in the main
method. The purpose of the Counter
class is to track how many times an action was performed. The like button in Facebook is a very good example of a counter. When this is the case, I should not let an external object to modify data in the variable total
. But in my program, the variable total
is directly accessible which makes it vulnerable and easy to manipulate.
// ... System.out.println("Count: " + counter.total); // prints 2 counter.total = 1000; System.out.println("Count: " + counter.total); // prints 1000 // ...
As you can see the above code, I added two more lines where I updated the value of total
to 1000
. Imagine Facebook has this code, no matter how many likes your post gets, at a certain time it will be reset to 1000. We don’t want this to happen do we? Let’s apply encapsulation.
Encapsulation is applied by adding access modifiers. I will be covering types access modifiers in a new post later. Check the updated code below.
class Counter { private int total; public void increment() { this.total++; } public int getTotal() { return total; } } public class Oopp { public static void main(String[] args) { Counter counter = new Counter(); counter.increment(); // increments count to 1 counter.increment(); // increments count to 2 System.out.println("Count: " + counter.total); // compile time error System.out.println("Count: " + counter.getTotal()); // prints 2 } }
Now, we changed the access specifier of the variable total
to private
which makes makes it inaccessible by outside objects. To access total
, the external object should use the publicly exposed method getTotal()
and this method is only accessible through the object counter
. We just hid the data total
of counter
from other objects. This is a way of hiding data of an object from other objects. Hence, encapsulation is also called as data hiding.
Abstraction
Abstraction is displaying only the required information and hiding the unnecessary information from the outside world. When applied to an object, it exposes only the required data to other objects and hides the rest.
For example, let’s assume that we are developing an online food ordering app like Swiggy or Zomato. We have to design a class for the customer who orders food. So I create a class Customer
as below.
class Customer { private String name; private String address; private int phoneNumber; private String favoriteMovie; private String collegeDegree; private String maritalStatus; // rest of the code }
The class Customer
has both relevant and irrelevant fields. A customer is a human and he/she can have fields like favoriteMovie
, collegeDegree
and maritalStatus
. But these fields are unnecessary for a food ordering app. The required data for our app are name
, address
, and phoneNumber
. By applying abstraction, we can move the unnecessary data to a parent class or interface with inheritance and keep the required data in the class Customer
.
class Human { String collegeDegree; private String favoriteMovie; private String maritalStatus; // rest of the code } class Customer extends Human { private String name; private String address; private int phoneNumber; // rest of the code }
In the above updated code, I have applied abstraction and moved the data irrelevant for a food ordering app to a parent class. Now, only the data required for our app are available in the class Customer
.
Polymorphism
In general, polymorphism means having more than one form. In programming, we can apply this to make something work in more than one way. For example, let’s consider that we have a classroom with a teacher and students. Here, both the teacher and the students are humans but in this scenario their roles differ. The teacher’s role is to teach and the role of a student is to learn. In general, humans do what they do to live their daily lives. But when being a teacher, the human teaches and likewise when being a student, the human learns. Same human show different behaviors in different situation. This is polymorphism.
Polymorphism can be achieved in two ways.
- Overriding
- Overloading
Overriding
Overriding is done between a parent class and it’s child classes. By overriding, parent class’s procedure can be given a new behavior in it’s child classes. Let’s take our teacher-student example.
class Human { public void performAction() { // live daily life } // rest of the code } class Teacher extends Human { @Override public void performAction() { // Teach students } // rest of the code } class Student extends Human { @Override public void performAction() { // Learn from teachers } // rest of the code }
In the above code, the method performAction
in the class Human
defines what a human does which is, in general, humans live their daily life. Down the hierarchy, in it’s child classes Teacher
and Student
, I have overridden the performAction
method to define new behavior. This is because, in the context of a teacher-student scenario, the behaviors of humans are to teach and learn. Now performAction
method for the Teacher
class defines that it should teach and for the Student
class defines that it should learn. Even though they both are humans, their behavior changes as per the context. Thus, applying polymorphism using overriding, we have defined different behaviors.
Overloading
Overloading allows us to define same method multiple times but each with different set of arguments. Let’s take one of our previous examples Counter
. We already have a method increment
which increments the total
by 1
. But now let’s assume I have a scenario, where I have to increment total
with a given value and not with 1
. In this case, I have to add another method to increment total
by the given value.
class Counter { private int total; public void increment() { this.total++; } public void increment(int delta) { this.total = this.total + delta; } public int getTotal() { return total; } }
In the above updated code, I added another method increment(int delta)
where delta
is the number I want to increment total
with. Note that both the methods have same name but different set of arguments. The same method increment
now has different definitions/forms. Each definition is distinguished by the number of parameters. This process is overloading. To be more precise, this process is functional overloading. We overloaded a method/function to have more than one form. In functional overloading, a method can be overloaded with,
- Different number of parameters in the argument list
- Different type of parameters in the argument list.
Below is a snippet for functional overloading where the method increment
is overloaded with number of arguments and types of arguments.
public void increment() { this.total++; } public void increment(int delta) { this.total = this.total + delta; } public void increment(String delta) { int intDelta = Integer.parseInt(delta); this.total = this.total + intDelta; }
Operator overloading
The other type of overloading is operator overloading. Similar to functional overloading, operator overloading overloads an operator. Most languages do not support operator overloading. You can find more about operator overloading here.
Advantages and disadvantages of Object Oriented Programming
Advantages
- Objects are modeled based on real-world entities which helps to write modular codes. While defining behavior of an object, the programmer doesn’t have to consider the behavior of other objects.
- Extensibility is another advantage of OOP. When we need to add more procedures or attributes to an object, the programmer can simply define a new one extending the older one.
- The fundamental concepts encourage reusability in OOP. An object can be reused across the project.
- Modularity, extensibility and reusability helps in faster and low-cost development.
Disadvantages
- Object Oriented Programming requires the programmer to have an object-oriented thinking which might not be easy for few.
- It is not suitable for all types of problems. It is more suitable for larger applications.
Object oriented programming and gaming
As a gamer, I wanted to add this part to this post 😁. Most of the video games are object oriented. I’d take Grand Theft Auto series for example. It has various objects like vehicles, weapons, people, mobile phones, etc. Each object have their own properties, procedures and a state. For example, let’s take a car.
A car in the game have it’s own color, top speed limit, type, durability, number of seats, etc. They can have procedures like accelerate, stop, open door, start, close door, explode, etc. We can even paint the car and it’s state will be maintained throughout the game or till it’s destroyed. The concepts of OOP would have applied for this code as well where class for Car would have inherited from a parent class Vehicle, overriding or overloading some of it’s methods.
What’s next?
Read how functions differ from methods in this post.
Check out other paradigms below.
I hope this post was helpful 😊. If you find this post informative, support us by sharing this with fellow programmers in your circle 😀.
For any suggestions, improvements, reviews or if you like us to cover a specific topic, please leave a comment.
Follow us on twitter @thegeeksclan and in Facebook.
#TheGeeksClan #DevCommunity