Background:
Let’s go through an example to understand some basics before we go to programming paradigms:
Problem statement:
We need to store all the even numbers occurring in an array
Solution 1
int[] array = {23, 24, 46, 58, 77, 87, 98};
int[] even = new int[array.length];
int j = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] % 2 == 0) {
even[j++] = array[i];
System.out.println(array[i]);
}
}
Solution2 :
int[] array = {23, 24, 46, 58, 77, 87, 98};
int[] even = new int[array.length];
even=Arrays.stream(array).filter(x -> x%2 == 0).toArray();
Reflection on the solutions
In solution 1, the programmer specified the exact steps to be taken to achieve the desired outcome i.e. Both what to be done and how to be done is specified
In solution 2, the programmer only specified what needs to be done and the implementation details were hidden(abstracted) i,e. only the what of things are specified and the how of things are upto the programming language library implementation and hence they are abstracted.
These are 2 distinct programming paradigms and the examples establish that there is conceptual difference between them. Both the examples are in Java and it is also a very good example of a programming languages which very recently implemented the provision for the second solution. Prior to Java 8, the solution 2 had no meaning.
Now lets get back to understanding the background.
Programming is the mechanism via which a developers interacts with a computer. Any programming language that supports programming is aimed to make developers express their mechanism of action to a computer.
Programming paradigms used to serve as a feature based classification mechanism for programming languages. Execution model, code organization, semantics are few of the key driving factors that serve as a differentiator between the paradigms. However in the recent years, programming paradigms have evolved into schools of thoughts
Why have programming paradigms anyway?
Certain paradigms are suited better for specific types of problems, so the choice of the programming language would be dependent on whether the language supports the solution to the problem or not.
Common programming paradigms:
Imperative programming paradigm
These involve giving instruction sets in detail to the computer to execute in a predefined order. The main reason for the nomenclature ‘imperative’ is due to the fact that programmers dictate exactly what the computer has to do in a specific way.
This paradigm is focused on the step by step description of the what and how of the program operation.
Example:
The imperative code might look like the one below:
int[] array = {23, 24, 46, 58, 77, 87, 98};
int[] even = new int[array.length];
int j = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] % 2 == 0) {
even[j++] = array[i];
System.out.println(array[i]);
}
}
It should be clear that we are instructing to iterate through each element in the array and compute the modulus of the number against 2 and then adding to another array.
Procedural programming paradigm
Language statements are structured as procedures (also called subroutines and functions). It is basically an extension of Imperative programming where the feature of procedures/function/subroutine is added.
The programmer is encouraged to divide the program execution into functions. This will help increasing the modularity and organization
Example:
The same imperative code is subdivided like the following:
public void initialiseArray() {
int[] array = {23, 24, 46, 58, 77, 87, 98};
}
public void findEven() {
int[] even = new int[array.length];
int j = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] % 2 == 0) {
even[j++] = array[i];
System.out.println(array[i]);
}
}
}
initialiseArray();
findEven()
It can be seen that the initialization of array and finding of even numbers and creating another array are now segregated and then are called in respective order to specify the flow of execution.
This increased the readability and one could just read the function calls in the end to understand the code. Please take in to account that we still don’t pass arguments to functions here and the code inside functions is the same imperative code.
Functional programming paradigm
This paradigm build on the procedural programming paradigm and functions are basically GODs here. They can be passed as arguments, assigned to variables, returned from other functions etc.
The ideology behind this was that a function should try utmost to rely only on it’s inputs for result generation and hence would produce the same output, should the inputs be same. This very aspect about absence of side effects is the motivation behind pushing for code to be written with functions
Example:
We would take the same imperative example and convert it to functional
int[] array = {23, 24, 46, 58, 77, 87, 98};
public int[] findEven(int[] array) {
int[] even = new int[array.length];
int j = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] % 2 == 0) {
even[j++] = array[i];
}
}
return even;
}
public void print(int[] arToPrint) {
for (int i = 0; i < arToPrint.length; i++) {
System.out.println(array[i]);
}
}
//while calling
print(findEven(array));
This implementation ensures that a function doesn’t modifies anything outside of it’s scope. It creates a local variable to process it’s own information.
Declarative programming paradigm
The main motive of this paradigm is to hide the implementation complexity and bring the languages close to human language. In a way, it is totally opposite of imperative programming and abstracts the programmer from the implementation details. The programmer only focusses on the what of result.
Example:
int[] array = {23, 24, 46, 58, 77, 87, 98};
int[] even = new int[array.length];
even=Arrays.stream(array).filter(x -> x%2 == 0).toArray();
Having a closer look at the filter method, we find that we are not explicitly instructing the computer to iterate over the array. We just instruct on the filter conditions and the results are automatically computed by the programming language during execution. It’s easier to read.
Object oriented programming paradigm
Separation of concerns is achieved through entities known as objects in this paradigm. Each entity possesses few properties (attributes) and actions (methods) that can be performed on those properties or by the class. Programs are designed by making them out of objects that interact with each other. Inheritance is generally used for the reuse of code
Objects sometimes correspond to things found in the real world. Each object is said to be an instance of a particular class. A lot of JAVA books talk about the various patterns inside OOP like Inheritance, Polymorphism, Abstraction, Encapsulation. I won’t bore delving into details.
Example:
We can’t take the previous example as it’s not very well suited to understand oop. So we will take another example: Representing a person
public class Person {
int age;
String name;
List<Person> friends; //represents a list of objects of the person class. friends of this person
Company currentCompany; // Represents an object of class company, where this person worlks
public void setAge(int age) {
this.age = age;
}
public void setName(String Name) {
this.name = name;
}
public List<Person> getFriends() {
return friends;
}
public void setFriends(List<Person> friends) {
this.friends = friends;
}
public Company getCurrentCompany() {
return currentCompany;
}
public void setCurrentCompany(Company currentCompany) {
this.currentCompany = currentCompany;
}
}
public class Company {
int estdYear;
public void setEstdDate(int estdYear) {
this.estdYear = estdYear;
}
public void getEstdDate() {
return estdYear;
}
}
In the caller class, an object of class Person will need to be created and it will represent a Person in a real world having attributes as mentioned in the class. The company class cannot change the internals of the Person class.
Conclusion
One programming language can offer multiple programming paradigms. Most recent programming languages like Java, Python, Ruby, JS support a variety of programming paradigms. It’s not worth obsessing over the debate of best programming language. Choose according to the use cases and talent pool.