I often read blogs about OOP in which various people discuss the merits of one theory/principle/pattern over another, or even how a particular theory/principle/pattern could be implemented, and there is one form of criticism which is becoming more and more prevalent:
If you don't do things the way that I tell you then your code is not OO, it is procedural.
When said in that way the words "your code is procedural", are a form of insult. They are saying that even though your code may be using objects, because you are not using them in the "proper" manner then it is not "true" OO code, it is procedural code which is pretending to be OO.
I consider this attitude to be a pile of bovine excrement, and in the following paragraphs I shall explain why.
Here are some of the criticism which I have found:
In Inheritance Is a Procedural Technique for Code Reuse the author says the following:
Inheritance is bad because it is a procedural technique for code reuse. .... That's why it doesn't fit into object-oriented programming.
Rubbish! Inheritance is one of the three characteristics which differentiates OO programming from no-OO programming, so saying that it doesn't fit into OOP just shows that you have lost the plot. Inheritance CANNOT be a procedural technique as it never existed in any procedural languages.
In Are You Still Debugging? the author says the following:
Code is procedural when it is all about how the goal should be achieved instead of what the goal is.
A method is procedural if the name is centered around a verb, but OO if it is centered around a noun.
Rubbish. A method/function/procedure is nothing but a label for a series of statements, and to quote Shakespeare:
What's in a name? That which we call a rose by any other name would smell as sweet.
It does not matter one iota what this label is called. It could be centered around a verb or a noun, or a verb and a noun, or a noun and a verb, or a random collection of letters and numbers. How you construct that name does not affect how the statements are executed, so to suggest that one form of this name is "procedural" while another is "oo" is just plain nonsense.
In this post a person called Fasda said the following:
The key you need to focus is THE WAY YOU THINK A SOLUTION.
In procedural you thinks solutions as writing a recipe, step by step. First do that, then that, and continue with...
In OO you think solutions as "people asking favors to others" -> Objects and Messages only.
I have already stated in a previous article that OOP is not about the passing of messages. The way that an object method is called is exactly the same as how a procedural function is called - it is always synchronous. In a true messaging system the activity requires the use of separate
receiveMessage() functions, and may be either synchronous or asynchronous.
In the same post he also said the following:
Try to use some pure OO language to stop thinking on IF, WHILE, FOR and all those keywords so common in procedural.
In If-Then-Else Is a Code Smell the author says the following:
In most cases (maybe even in all of them), if-then-else can and must be replaced by a decorator or simply another object.
The idea that you cannot use IF, WHILE, FOR, etc in an OO program is too ridiculous for words. Where have the alternatives for these constructs been documented? Do they even exist?
Fortunately there are some sane people out there who do not go along with this idea. In his article Pragmatic OOP Ricki Sickenger had this to say:
I have met programmers who believe that anywhere there is a conditional statement in OO code, there is cause to subclass, "because that is the OO way!". And they will defend it against any pragmatic reasoning. So anywhere you see an if/then/else or a switch statement, you should find a way to break the logic into separate objects to avoid the logic. The dogma here is that conditional statements complicate things and are not strictly OO, so they must be minimized and preferable erased.
In this post a person called Goop said the following:
What you fail to realize is that scripting the flow of control in order to solve problems is absolutely anathema to OOP.
Then how does control flow from one group of instructions in an object method to another? Do you just write a number of methods and let the machine decide in what order they should be executed? Do you construct a mass of ravioli code and throw it into the air and let control flow from one piece of pasta to another in a random sequence?
The worst idea of all came from Getters/Setters. Evil. Period. in which David West wrote:
Step one in the transformation of a successful procedural developer into a successful object developer is a lobotomy.
This tells me that OO programmers are just like procedural programmers, but with part of their brains removed.
While there are some features which are common to both the procedural and OO paradigms, there are some which only exist in OO languages and MUST be present in order to allow a language to be called Object Oriented. These features are Encapsulation, Inheritance and Polymorphism. Although many optional extras have been added since OO languages became popular, I consider them to be nothing more than embellishments which, depending on your point of view, are either Gilding the Lilly or Gilding the Turd.
However, it should be noted that simply having different features is not good enough. Neither programmers nor their employers will switch to using a different language unless there is some benefit in doing so. The supposed benefit of OOP, as I have previously stated here, is that it "increases code reuse and decreases code maintenance". I say "supposed" for the simple reason that far too many programmers are following a corrupted set of "rules" which cause them to fail to meet this simple objective.
|Encapsulation||The act of placing data and the operations that perform on that data in the same class. The class then becomes the 'capsule' or container for the data and operations. This binds together the data and functions that manipulate the data.
Note that this requires ALL the properties and ALL the methods to be placed in the SAME class. Breaking a single class into smaller classes so that the count of methods in any one class does not exceed an arbitrary number is therefore a bad idea as it violates encapsulation and makes the system harder to read and understand. Putting all methods which are related into the same class leads to high cohesion whereas putting related methods into separate classes leads to low cohesion.
Note that data may include meta-data (type, size, etc) as well as entity data.
Please also refer to What Encapsulation is not.
You may also like to read the following:
OO languages have the following characteristics:
This is demonstrated in the following example:
$object1 = new foobar; $object2 = new foobar; $result1 = $object1->method1(...); $result2 = $object1->method2(...); $result3 = $object1->method3(...); unset($object1); $result1 = $object2->method1(...); $result2 = $object2->method2(...); $result3 = $object2->method3(...); unset($object2);
Here you can see that each object is available for method calls between its instantiation (using the "new" keyword) and its destruction (using the "unset" keyword).
Procedural languages have the following characteristics:
This is demonstrated in the following example:
$result1 = function1(...); $result2 = function2(...); $result3 = function3(...);
|Inheritance||The reuse of base classes (superclasses) to form derived classes (subclasses). Methods and properties defined in the superclass are automatically shared by any subclass. A subclass may override any of the methods in the superclass, or may introduce new methods of its own.
Note that I am referring to implementation inheritance (which uses the "extends" keyword) and not interface inheritance (which uses the "implements" keyword).
Please also refer to What Inheritance is not.
In order to make use of the code sharing technique called inheritance you must first define the properties and methods which are to be inherited. This is called an abstract class. Then you can create any number of concrete classes which merge this definition with more specific details. This is where the unknown database table is transformed into a specific database table.
Note that there is no limit on the number of times which an abstract superclass can be inherited by a concrete subclass. In my own application I currently have over 350 database tables, so that is 350 cases of inheritance.
Procedural languages do not have inheritance. There is no way in which the code in one function can be merged with additional code to form another function. You can call another function, but you cannot share or modify its contents.
|Polymorphism||Same interface, different implementation. The ability to substitute one class for another. This means that different classes may contain the same method names, but the result which is returned by each method will be different as the code behind each method (the implementation) is different in each class.
Note that this does NOT require the use of the keywords "interface" and "implements" as these are totally optional in PHP. All that is required is that different classes implement the same method name with the same signature.
Please also refer to What Polymorphism is not.
To take advantage of polymorphism you must have code which calls one or more methods on an object, but where the identity of the object is not known. In this way you can reuse this code with a different object and it will produce different results. Here is an example of such code:
script: std.enquire1.inc <?php .... include "$table.class.inc"; $object = new $table; $data = $object->getData($where); .... ?>
All that is required is a mechanism to supply the contents of $table. This can be done using a number of scripts constructed like the following:
script: foobar(enq1).php <?php $table = 'foobar' include 'std.enquire.inc'; ?>
script: snafu(enq1).php <?php $table = 'snafu' include 'std.enquire.inc'; ?>
As you should be able to see, if I have a separate one of these scripts for each database table, and I have 350 tables, it means that I can reuse the contents of std.enquire1.inc 350 times. This is possible because all my page controllers, of which std.enquire1.inc is just a small sample, use only those methods which were defined in the abstract class, which means that they can be used with anything which inherits from this class.
Procedural languages do not have polymorphism. Each function exists only once, therefore there cannot be more than one implementation with the same name, so it is notpossible to swap one implementation with another.
When people say that OO programming is completely different from procedural programming and it requires a totally different way of thinking I'm afraid that they are completely wrong. Yes, there are differences, but there are also similarities. The differences are not concerned with the code that you write to do stuff, but only how that code is packaged. With Procedural languages you can package your code into functions, procedures or subprograms, but with OO languages you can package your code into classes and methods. The use of classes then allows for inheritance and polymorphism which are techniques for reusing code that are not available in procedural languages. This simple observation leads me to make the following statement:
OO programming is exactly the same as procedural programming except for the addition of encapsulation, inheritance and polymorphism.
I am not the only one who shares this opinion. In his article All evidence points to OOP being bullshit John Barker says the following:
Procedural programming languages are designed around the idea of enumerating the steps required to complete a task. OOP languages are the same in that they are imperative - they are still essentially about giving the computer a sequence of commands to execute. What OOP introduces are abstractions that attempt to improve code sharing and security. In many ways it is still essentially procedural code.
In his paper Encapsulation as a First Principle of Object-Oriented Design (PDF) the author Scott L. Bain wrote the following:
Object Orientation (OO) addresses as its primary concern those things which influence the rate of success for the developer or team of developers: how easy is it to understand and implement a design, how extensible (and understandable) an existing code set is, how much pain one has to go through to find and fix a bug, add a new feature, change an existing feature, and so forth. Beyond simple "buzzword compliance", most end users and stakeholders are not concerned with whether or not a system is designed in an OO language or using good OO techniques. They are concerned with the end result of the process - it is the development team that enjoys the direct benefits that come from using OO.
This should not surprise us, since OO is routed in those best-practice principles that arose from the wise dons of procedural programming. The three pillars of "good code", namely strong cohesion, loose coupling and the elimination of redundancies, were not discovered by the inventors of OO, but were rather inherited by them (no pun intended).
Cohesion is the degree to which the responsibilities of a single module/component form a meaningful unit. High cohesion is considered to be better than low cohesion.
Coupling is the degree of interaction between two modules. Whenever you have one module calling another you have coupling. Loose coupling is considered to be better than tight coupling.
Elimination of redundancies is aimed at removing code that you do not need and is now called the YAGNI principle.
Other best practices which evolved in procedural languages, but which are still relevant in the OO world, are the KISS and DRY principles
Here is a summary of the similarities and differences:
This also tells me that OOP can be adequately supported in procedural languages (such as PHP and COBOL) which have had the necessary syntax added in to enable encapsulation, inheritance and polymorphism without replacing the original syntax with something which is more OO-like. For example, if a procedural language allows a statement such as
$result = uppercase($string) it would seem to be overkill to replace it with
$result = $string->uppercase(). The result is exactly the same, but a lot of effort has been expended just to do it differently.
OO theory is constantly being expanded to include more and more concepts, and these concepts are becoming more and more complicated. As languages are modified to include these add-on concepts newcomers to these languages become convinced that it is these add-ons which define what OO is. I totally disagree. OOP does not require the use of any of these optional extras, so it is wrong to say that a program is not OO simply because it does not use them. It would be like saying that a car is not a car unless it has climate control and satnav. Those are optional extras, not the distinguishing features, and not having them does not make your car not a car. It would also be incorrect to say that a car is a car because it has wheels. Having wheels does not make something a car - a pram has wheels, but that does not make it a car, so having wheels is not a distinguishing feature.
There seems to be a theory among OO programmers that all OO code is superior to procedural code by virtue of the fact that it is OO. In theory they should be correct, but in practice they are not. Just as it is possible to produce spaghetti code (unstructured branching using GOTO) in a procedural language it is also possible to produce ravioli code (too many small classes) or lasagne code (too many layers) in an OO language. Using the features that the language provides will not guarantee "good" code, it is how you make use of those features which is the deciding factor.
It is possible to write the same program in both a procedural style and an OO style, and for the results to be identical. If both have the same UI then it will not be possible for the user to identify which programming style was used. Does this make it an example of "implementation hiding"? This simple observation proves that the underlying code which "does stuff" is more or less the same, but it is only how this code is packaged which is different. When dealing with classes and objects there are three important questions to answer:
If you create too many objects, especially non-reusable objects, in a structure that is more complicated than it need be, then you may end up by actually being less productive than you could be. Your code may be more difficult to read and understand and therefore more difficult to maintain, which then totally negates the benefits of using OOP in the first place. If you cannot use the facilities that OO languages provide and create cost-effective software, and more importantly, software that is more cost effective than what can be produced using non-OO languages, then as far as I am concerned you have failed. Those OO features were added to languages in order to provide certain benefits, and if your work does not show those benefits then as far as I am concerned you are doing it wrong.
Anybody who says that OO programming is totally different from its procedural predecessors has been seriously misinformed. It is in fact exactly the same except for some minor differences. The code that you write to "do stuff" is fundamentally identical, with the only exception being how it is packaged. In procedural languages you wrap your code in plain functions, but with OO languages you have classes and methods, and classes provide access to new ways of sharing and reusing code - inheritance and polymorphism. It is this ability to provide an increase in reusability and therefore maintainability which is supposed to give OOP the edge over its procedural predecessors, but too many programmers think that there is some sort of magic involved in OOP and invent wierd sets of rules which, if not followed to their satisfaction, causes them to brand your code with what they regard as the ultimate insult - "That's not proper OO, that's procedural".
Do not think for one minute that you cannot possibly be wrong because you are following all the rules set by the programming elite. Far too many of these "rules" were created by pseudo-intellectuals whose purpose was to show how clever they are rather than providing simple solutions to complex problems. They spend so much time in nit-picking arguments over their extreme interpretations of various programming principles that they lose sight of the big picture. They concentrate too much on how the results should be achieved instead of what the results should be. If you don't belive me I suggest you take a look at the following:
The following articles were written by me.
© Tony Marston
20th April 2017