I used to think that using classes in my code meant that I was using an Object-Oriented approach. After some time, I realized I was mistaken.
While I was reading the book "Clean Code" by Robert C. Martin, some examples took me by surprise. There were some examples used for demonstrating the procedural coding style and OO coding style that were using classes and methods. This article will share the insights that I have learned since then.
Wikipedia defines procedural programming as:
From this definition, it is asserted that procedural programming is really just specifying a set of ordered steps needed to implement the requested functionality. It also implies that those steps are implemented as a detail that's not related to the paradigm. The important thing is that it's imperative in how it works.
Let's look at a few examples:
The below code is procedural-
In a procedural programming language, a program basically consists of a sequence of instructions. This tells the computer to do something, and functions are written to accomplish a specific task. In this example, we have created different methods for calculating areas of different geometries.
Now the approach will remain procedural, even if it uses an object:
Even though we are using the object paradigm, we are following the same structure. We still have a sequence of instructions and functions for accomplishing different tasks.
This is still procedural, even though it uses a class:
Now, even though we have created the class for different geometry, it is still procedural. Procedural programming doesn't have anything to do with whether we use classes or not; it's a programming approach.
The three codes mentioned above use the same programming approach. The only difference between them is the way the routines are resolved. Still, each code is procedural. Each one has discrete steps that must be taken into consideration.
Let's take a look at what the OOP approach for this program will look like –
According to Wikipedia, Object-Oriented Programming is –
The concepts that we can take out from here are –
-It must abstract the data concepts into modular units.
-It must have some way to execute code polymorphically.
-It must at least partially encapsulate that code and functionality.
Now let's implement these concepts on the above example –
What's the difference?
Objects used in OOP hide the data behind abstractions and expose functions that operate on them.
In the first procedural programming approach, classes "Square," "Rectangle," and "Circle" are exposing their data attributes, "side," "height and width," and "center and radius," respectively. The class Geometry can directly work and access their data attributes. Indeed it would expose implementation even if the variables were private, and we were using variable getters and setters.
Hiding implementation is not just putting a layer of functions between the variables. Hiding implementations is about abstracting behavior. A class does not push its variables out through getters and setters. Instead, it exposes abstract interfaces that allow users to manipulate the essence of data without even knowing its implementation. As it is implemented in the OOP approach example, each class has its own area functions. Now, no class other than the respective shape class knows about the implementation of calculating the area. The classes expose "area ()" functions that operate on their respective data attributes and provide the resulting area.
Now let's go back to the OOP example and revise the concepts we have taken out from that definition of Wikipedia for OOP.
-The OOP approach example is abstracting the data concepts into modular units.
-The OOP approach example is using the "area ()" method polymorphically.
-And the data and the methods are encapsulated together.
So if someone is using an object, it should not be assumed that they are writing OOP. Likewise, if someone is using functions, that does not mean they are necessarily using procedural programming.
What to use when?
Now suppose we want to calculate Parameters also. The respective shape (Circle, Square, and Rectangle) will be unaffected, and any other class that depends on them will also remain unaffected. But if I add a new class for a shape like Triangle, I must change all Geometry functions to deal with it. However, in the case of the OPP approach, none of the existing functions are affected. But if I add a new function, all of the shape classes will change.
There is a complementary nature of these two approaches. As it is summarized in the book Clean Code by Robert C. Martin -
"Procedural code makes it easy to add new functions without changing the existing data structures, OO Code, on the other hand, makes it easy to add new classes without changing existing functions."
So, we can conclude that when we want to add new data types rather than new functions, the OO approach is appropriate. On the other hand, when we want to add new functions instead of data types, the procedural code is more appropriate.
Other aspects to consider include performance vs. maintainability. If one intends on having a specialized program that won't have change requirements and is designed for high performance, their efforts will be better served building with PP concepts. A short script like programs that run against crontab, Windows Powershell, or Launchctl shines here.
Procedural vs. OOP is an approach to writing code, not how one writes it. If someone focuses on "Steps" and an ordered way of writing a program, he is more likely to write a procedural code. But if someone is focusing on state transformations and encapsulated abstractions, he is writing OOP.
The choice of usage from these approaches depends upon the application we want to write. It depends upon aspects such as Procedural performance, maintainability, reusability, etc.