In October this year I came across two articles written by Yegor Bugayenko called What's wrong with OOP and MVC vs. OOP in which a person calling himself Hall of Famer made certain statements which I deemed worthy of comment. Below is a summary of the exchanges which followed.
In this post Hall of Famer said the following:
There is something very wrong with procedural programming, with procedural programming you always end up with amateurish spaghetti code.
Having spent over a decade using that well-known procedural language called COBOL, and having being on a Jackson Structured Programming (JSP) course and learned about Modular Programming and Structured Programming I considered his statement to be flawed, so I countered his view with this reply in which I stated the following:
I disagree. The opposite of spaghetti code is structured code, and it is possible to write structured code in a procedural language just as it is to write spaghetti code in an OO language.
Hall of Famer then replied with this:
Nope it is not, procedural programming always leads to unstructured spaghetti code. If you write in a procedural language with modular design approach, it is actually OO design. Procedural design is spaghetti code, theres no way to write good procedural code. Again note the difference between procedural syntax and procedural design. With C you have to stick with procedural syntax with no OO support natively, but the design approach still can be OO.
I responded with this:
Did you come up with that idea yourself, or can you point to any articles which support that claim? The definition of spaghetti code includes the word "unstructured", so procedural code which is properly structured is NOT spaghetti code. Procedural code which is structured is NOT OO design as OO requires encapsulation, inheritance and polymorphism. Structured programming, as taught by Michael Jackson, is based on sequence, iteration and selection. Besides, OO design did not exist in the 1970s and 80s when I was using COBOL.
He immediately came back with the following:
Not really, many people have thought of this in the past. The OO design approach really, doesn't necessarily require OO syntax. It is a methodology, and the OO mindset is far more important than OO syntax itself. Just because you use objects doesn't mean you write OO code, procedural code encapsulated in objects ain't OO code.
And nope, there is no way to write properly structured procedural code. Procedural code = spaghetti code, you again had a confusion between procedural code and code written in procedural language. By properly structured I mean application-level design and architecturing, not just writing structures such as control structures, loops and functions. Just because your application has a structure, doesn't mean its 'properly' structured.
I could not let such ridiculous statements go by, so I replied with the following:
The OO design approach really, doesn't necessarily require OO syntax.You are confusing OO design with OO programming. A design is, or should be, language agnostic as it describes the application before it is built. A good design should be able to be implemented in any language, procedural or OO.
OO programming can only be achieved with a language which directly supports the OO principles of encapsulation, inheritance and polymorphism. If you use those features then your code is OO. If you don't use those features then your code is not OO.
procedural code encapsulated in objects ain't OO codeAll code is executed in a linear fashion whether it is in a procedural function or a class method, so there is no effective difference between procedural code and OO code when it is being executed. OOP requires the use of specific features of the language as described above.
there is no way to write properly structured procedural codeThere is no such thing as "properly structured", there is either "badly structured" or "well structured". "Properly structured" is an entirely subjective description which means different things to different people. Spaghetti code is code which either has no structure at all, or it is badly structured. It is possible to write COBOL code which is well structured just as it is possible to write Java code which is badly structured spaghetti.
you again had a confusion between procedural code and code written in procedural languageI disagree. If you write code in a procedural language it is procedural. If you write code in an OO language and implement encapsulation, inheritance and polymorphism then your code is OO.
Let me expand on some of the points he has made so far:
If you write in a procedural language with modular design approach, it is actually OO design
I disagree. Modular programming was practiced in procedural languages long before OOP and OOD came on the scene. OO design requires the use of Object Oriented Concepts such as Objects/Classes, Information hiding, Inheritance, Interfaces and Polymorphism. Modular design does not, so to say that the two are the same is plainly wrong.
The OO design approach really, doesn't necessarily require OO syntax
I disagree. If you look at the description of Object Oriented Concepts in OOD you will see that it requires support for OO concepts in the language. None of these concepts existed in the COBOL versions that I used, so I cannot see how it is possible to use OOD for a language that does not support OO concepts.
It is a methodology, and the OO mindset is far more important than OO syntax itself.
I disagree. OOP is not a state of mind, it is an implementation of OO concepts. You cannot implement OO without OO syntax, and if you use OO syntax in your programs then it is OOP.
Just because you use objects doesn't mean you write OO code
I disagree. If you write programs which are oriented around objects then your are, by definition, doing Object Oriented Programming. It may not be the most efficient or the most effective, but it is still OO.
Code which has a structure is not 'properly' structured
I disagree. Either code has a discernible structure which can be shown in a structure chart or it does not. It is either spaghetti code (or ravioli code in the case of OOP) or it is not. This is a boolean condition in which the result is either TRUE or FALSE, YES or NO. There is no "yes it is but no it isn't".
This person does not understand the difference between "procedural" and "object oriented". There is a large amount of functionality which exists in both. OO code is exactly the same as procedural code except for the addition of encapsulation, inheritance and polymorphism. Both paradigms have lines of code containing statements which are executed in a linear fashion. Both paradigms support expressions, operators, control structures, built-in functions and user-defined functions. Both paradigms support the concept of Modular Programming and Structured Programming. Only one paradigm supports encapsulation, inheritance and polymorphism.
In my reply I also responded to a previous question of his:
Oh yeah, do you happen to be the same Tony Marston who made a fool of himself on SitePoint some time ago?I have often been criticised for my different views, which have sometimes been described as heretical, but I am allowed to have a different view, just like everyone else on this planet. I do not care that my views are different as I write software which pleases my paying customers, not a bunch of ignorant or confused developers.
He followed up with this reply:
I see, so you really are that Tony Marston who keeps embarrassing himself. Of course you are allowed to have different viewpoints, different doesn't necessarily mean bad. But your viewpoints are mostly incorrect, inferior and confused, yet you call the other developers ignorant and confused developers, when you are exactly ignorant and confused yourself. People criticize you not because you have those incorrect opinions, but that you try to convince them that your opinions are good when they clearly are bad. This is exactly why everyone was against you in that Singleton vs Dependency Injection thread.
For those of you who are in the dark, my opinions on dependency injection were published in Dependency Injection is EVIL. If you read it carefully you will notice that I actually say that DI can be beneficial in appropriate circumstances, but that it should not be applied indiscriminately. In my applications there are places where I DO use DI, but there are also places where I DO NOT.
I responded to his post with this:
I see, so you really are that Tony Marston who keeps embarrassing himself.I am not embarrassed in the least. I find the criticism of my work to be very amusing. I sometimes laugh so much I can feel the tears running down my trouser leg.
Of course you are allowed to have different viewpoints, different doesn't necessarily mean bad.How very kind of you. It makes a change from being told that my opinions are bad simply because they are different.
But your viewpoints are mostly incorrect, inferior and confusedThen how come the code which I write using my "incorrect, inferior and confused" methods still produces applications which not only work, but work very well. All without the use of those abominations called dependency injection and object-relational mappers.
In this post I made the following statement:
I said your opinions are bad because your opinions are incorrect, outdated and confused, not because they are different.My opinions are not incorrect for the simple reason that the code which I create most definitely works, and has done for over a decade. My opinions are not outdated simply because I refuse to accept all the add-on definitions to OOP which have been dreamt up by a bunch of pseudo intellectuals. OOP consists of nothing more than encapsulation, inheritance and polymorphism, and everything else is an optional extra.
I have made a list of the OO add-ons which I ignore in A minimalist approach to Object Oriented Programming with PHP.
He made yet more erroneous statements in this reply to which I responded with the following:
Well just because your code works for you, doesn't mean your opinions are correct.If my methods produce cost-effective software which works then those methods cannot be wrong.
Your code works for your legacy applications/frameworksIf by "legacy" you mean "mature" and "proven" then that is precisely what my customers want. Large corporations do not want leading edge, bleeding edge, immature and unproven applications, they want something with a pedigree. That is what I provide.
your legacy PHP 4 applicationFYI it currently runs on PHP 7, and has run through all versions of PHP 5. It is as current as it needs to be.
And those are NOT add-on definitions of OOP, they are fundamental and universally agreed concepts.I suggest you look up the dictionary definition of "fundamental". In http://dictionary.cambridge.org/dictionary/english/fundamental it is described as "forming the base, from which everything else develops". The original definition of OOP by Alan Kay (who invented the term) specifies nothing more than encapsulation, inheritance and polymorphism, so everything which came after that IS an addition and IS optional. The fact that some people consider that those optional extras are an essential part of OOP just shows that it is THEY who are confused, not me.
In fact, the more confused, old-fashioned and incompetent you are, the more counter-examples I will have when I teach people how to avoid bad habits. In a way, I should thank you for this.And I should thank you for encouraging techniques that will prevent any programmer from creating software which will be a serious competitor to mine. The true purpose of a software developer is to develop software which impresses the paying customer with its cost-effectiveness, not its ability to impress a bunch of developers with its fashionable yet overly-complex implementation. I sell my enterprise application to large corporations all over the world, so next to that monster which you and your cohorts would produce my software would appear sleek and slim.
In this post he made the following statements:
Its funny that you referenced Alan Kay when you did not even understand what Alan Kay's vision of Object and OOP are. Alan Kay's idea for Objects are actor models, which communicate with each other by sending messages. Even with the 3 basic characteristics of OOP you still fail miserably. Your singleton breaks encapsulation, your inheritance is totally wrong when you inherit 9000 lines of base class, and your polymorphism is nowhere to be found. Either way you are confused about OOP and you are at the bottom of your heart, a procedural programmer.
I asked him to provide a link to any article written by Alan Kay in which he said that OOP is about sending messages, but he could not.
I asked him to provide a link to any article which explained how a singleton could possibly break encapsulation, but he could not.
In this post he made some more questionable statements:
Yes, your singleton breaks encapsulation, it always does. When you use singleton, the object stored as singleton becomes a glorified global variable. When your client code uses singleton, it becomes a hidden dependency that cannot be tested or maintained. Yes, your inheritance is done wrong for that 9000 lines of God class, because your parent class breaks SRP and your child classes are mere dumpers for the garbage from this god parent class. And your polymorphism is indeed nowhere to be found, because you have done inheritance wrong in the first place, and blindly giving all responsibilities to all child classes ain't the right way to do polymorphism.
How so? In OOP inheritance is the primary method for sharing reusable code. In my main enterprise application there are over 350 database tables for which I have a separate class. Each class contains the business rules for a single designated database table. There is a lot of code which I could duplicate in each table class, but instead I moved all that code into an abstract table class which is then inherited by every concrete class. There is no limit to the amount of code which can be shared this way. How can this possibly be the "wrong" use of inheritance? There is NO limit on the amount of code which can be inherited. In fact, if you looked at the objectives of OOP you would see that it actually encourages the creation of more reusable code, not less.
Then you obviously haven't looked very far. Polymorphism becomes available when the same method signature is shared by many different objects, which then allows a piece of code which calls that method to work on any object which implements that method. In my framework every one of my page controllers calls methods on each Model object which are defined in the abstract table class. As I pointed out in the previous section that abstract class is inherited by every one of my 350 table classes, which means that every one of my 40+ Controllers can be used with every one of my 350 Models. How can this situation NOT be described as polymorphism?
Now do the maths - if I have 40+ controllers each of which can be used on any of my 350 Model classes then that results in 40x350=14,000 opportunities for polymorphism. Do you see that - FOURTEEN THOUSAND instances of polymorphism which you failed to spot. Are you blind, or just intellectually challenged?
I disagree. The definition of a God object contains the following:
A common programming technique is to separate a large problem into several smaller problems (a divide and conquer strategy) and create solutions for each of them. Once the smaller problems are solved, the big problem as a whole has been solved. Therefore a given object for a small problem need only know about itself. Likewise, there is only one set of problems an object needs to solve: its own problems.
In contrast, a program that employs a god object does not follow this approach. Most of such a program's overall functionality is coded into a single "all-knowing" object, which maintains most of the information about the entire program, and also provides most of the methods for manipulating this data. Because this object holds so much data and requires so many methods, its role in the program becomes god-like (all-knowing and all-encompassing). Instead of program objects communicating among themselves directly, the other objects within the program rely on the single god object for most of their information and interaction. Since this object is tightly coupled to (referenced by) so much of the other code, maintenance becomes more difficult than it would be in a more evenly divided programming design. Changes made to the object for the benefit of one routine can have unintended effects on other unrelated routines.
A god object is the object-oriented analogue of failing to use subroutines in procedural programming languages, or of using far too many global variables to store state information.
Saying that my abstract class is a God object simply because of its size proves nothing except that you can count, but that you either cannot read or cannot understand what you read. My abstract class does not exhibit the characteristics or symptoms of a God class as described in that article, so your accusation is without substance.
My abstract class may have more methods than you are used to, or more than you can possibly imagine, but that's simply because your imagination is smaller than mine, your capabilities are smaller than mine, and your applications are smaller than mine. Or, to put it another way, compared to mine your imagination is puny, your intellect is puny, and your applications are puny. If you don't have the mental capacity to deal with a class which has that number of methods, then how can you have the mental capacity to deal with the same number of methods in multiple classes? You are not increasing the readability of the code, you are replacing highly cohesive code with ravioli code, which makes navigation through the code for maintenance purposes more difficult.
One of the characteristics or symptoms of a true God class is that if the application expands then the God class expands with it. The application cannot be expanded without amending the God class. This situation does not exist my framework. In the past 10 years my enterprise application has expanded from 35 database tables to over 350, and as my so-called God class does not contain any references to any tables it is totally unaffected by the number of tables. When I add a new table to my application all I do is create a new table class which inherits from, not expands that so-called God class. I then create as many user transactions as I want from my library of Transaction Patterns, each of which will reference that table class using generic methods which were defined in that so-called God class. Did you read that? Each new table class inherits from the so-called God class, it does not cause it to expand in the slightest. So if that class does not expand when the application expands it does not suffer the symptoms of a God class, therefore it does not deserve to be called one.
You may be confused by the fact that in order to implement inheritance you have to use the word "extends" in your code, as in
concrete_class extends abstract_class, and this word can have different meanings depending on the context. For example, if you "extend" a house by adding on a conservatory, a new wing or new floor, the end result is that the house itself becomes bigger. This is not what happens in OOP. The word "extends" should have been replaced with "inherits" so that what you see is that the original abstract class is completely unchanged, and that what you have actually done is create a totally new class which incorporates a copy of the abstract class, but with some additions. To continue with the house analogy, the original house is unchanged, but what you end up with is a copy of that house with the addition of a conservatory, wing, or floor.
This sort of confusion is not new in the software world. Do you remember Hungarian Notation? This was invented by a Microsoft programmer called Charles Simonyi, and was supposed to identify the kind of thing that a variable represented, such as "horizontal coordinates relative to the layout" and "horizontal coordinates relative to the window". Unfortunately he used the word type instead of kind, and this had a different meaning to those who later read his description, so they implemented it according to their understanding of what it meant instead of the author's understanding. The result was two types of Hungarian Notation - Apps Hungarian and Systems Hungarian. You can read a full description of this in Making Wrong Code Look Wrong by Joel Spolsky.
I disagree. The Single Responsibility Principle (SRP), also known as Separation of Concerns (SoC), states that an object should have only a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility. But what is this thing called "responsibility" or "concern"? How do you know when a class has too many and should be split? When you start the splitting process, how do you know when to stop? In his article Test Induced Design Damage? Robert C. Martin (Uncle Bob) provides this description:
How do you separate concerns? You separate behaviors that change at different times for different reasons. Things that change together you keep together. Things that change apart you keep apart.
GUIs change at a very different rate, and for very different reasons, than business rules. Database schemas change for very different reasons, and at very different rates than business rules. Keeping these concerns (GUI, business rules, database) separate is good design.
I don't know about you, but I recognise the separation of GUI, business rules, and database access as the description of the 3-Tier Architecture, upon which my framework is based. Not only that, because I have also split my Presentation layer into two smaller components, a Controller and a View, it is also an implementation of the Model-View-Controller design pattern. This produces the structure shown in Figure 1:
Figure 1 - The MVC and 3-Tier architectures combined
My abstract class is inherited ONLY by Model classes, and contains absolutely NO logic which rightly belongs in a Controller, View or DAO, so for you to say that it does too much and therefore breaks SRP cannot be supported by the facts. It does a lot, and perhaps it does more than you are used to, but that is no reason to assume that it breaks SRP. Besides, if I were to break that abstract class into smaller units I would be breaking encapsulation and the concept of cohesion, and those would be mistakes of far greater magnitude.
You should also note that SRP is all about the separation of logic, not the separation of information. Logic is code while information is data. Taking the data for an entity and splitting it across several classes would violate encapsulation and would therefore be wrong. You should also note that you should not split the logic across several objects unless you can give that logic a sensible (and short) name and fit it into a structure diagram as shown in Figure 1. Such terms as "Service" and "Helper" would be meaningless. If your method produces a structure that has so many objects that you cannot fit it into a single page diagram, or you cannot give each object a meaningful name, then you have probably gone too far. Splitting a monolithic piece of code into smaller parts is a good idea, but you should have the intelligence to know what to separate out and when to stop separating. The idea that you should extract until you drop I consider to be too ridiculous for words.
How can I possibly say such a thing? The principle of encapsulation can be described as follows:
The act of placing an entity's 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.
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. It would also decrease cohesion and increase coupling which would be the exact opposite of what should be achieved.
As you should be able to see, if I moved some methods to another class then I would be ignoring the ALL part of that description, and as far as I am concerned that cannot be justified under ANY circumstances.
Note that this principle makes no mention of size, so I regard any such limitation as artificial and feel no obligation to be bound by it.
How can I possibly say such a thing? The principle of cohesion can be described as follows:
Cohesion is a measure of the strength of association of the elements inside a module. A highly cohesive module is a collection of statements and data items that should be treated as a whole because they are so closely related. Any attempt to divide them would only result in increased coupling and decreased readability.
Simply put, this states that functions/methods which are related should be contained within the same class, and that a class should only contain functions/methods which are related. Putting all functions into a single class would be just as wrong as putting each function into a separate class. The correct grouping of functions requires a modicum of intelligence, and all evidence points to the fact that this is missing in the greater programming community. Only the intelligent few seem to have this ability, while the remainder are nothing more than Cargo Cult programmers who are going through the motions and assuming that what they are doing actually works as intended because they are incorporating all the right buzzwords and jumping on the same bandwagon as all the other lemmings.
As you should be able to see, if I moved some methods to another class then I would be decreasing cohesion and increasing coupling, and as far as I am concerned that cannot be justified under ANY circumstances.
Note that this principle makes no mention of size, so I regard any such limitation as artificial and feel no obligation to be bound by it.
There follows a long exchange of argument and counter-argument which proves nothing except for the fact that we disagree on almost everything. He then adds to his list of stupid arguments in this post in which he said the following:
I already looked at your application architecture, and although it breaks down into 3 tiers, each tier of your application still contains a handful of god classes that do too much.
Excuse me, it is simply not possible for an application to have more than one God class. The definition of a God class clearly states that it contains "Most of a program's overall functionality" (note the use of the word "most") so if there is more than one such class then the term "most" no longer applies and has be be reduced to "some". If the term "most" no longer applies then it no longer qualifies to be called a God class according to the true definition of a God class. You are not allowed to redefine words in order to promote your personal agenda.
In this post he said the following:
And nope, you don't understand SOLID principles. You don't even understand SRP, or maybe you understand SRP but breaks it anyway. All the principles exist for good reasons, because it makes development easier, faster and more cost-effective.
This is where I have to strongly disagree. I have read many articles which supposedly show how a particular principle can be used, but all I see is the extra code that needs to be written for no apparent benefit. When I say "no apparent benefit" I mean that that the code still does what it did before the principle was applied, but with more code, with more levels of indirection. More code means longer to write, longer to run, longer to read and therefore more difficult to understand and maintain. When you can show me a principle which results in less code being written I will sit up and take notice, but until then I shall treat it as a waste of time.
In my long career in software development I have come across many ideas put forward by many people as being the silver bullet that will solve all the current problems, but the biggest problem with these ideas is that they are usually badly written, are too vague and imprecise, and are open to interpretation and therefore mis-interpretation. The levels of mis-interpretation can vary between "extreme" and "perverse". The first big problem is therefore which interpretation do you choose? Is it the "moderate" or "extreme" version? When do you apply the idea? When do you stop applying the idea? This leads me to the following observation:
There are two ways in which an idea can be implemented - intelligently or indiscriminately.
Those who apply an idea or principle indiscriminately, who apply it in inappropriate circumstances, or who don't know when to stop applying it, are announcing to the world that they do not have the brain power to make an informed decision. They simply do it without thinking as they assume that someone else has already done all the necessary thinking for them. This leads to a legion of Cargo Cult programmers, copycats and buzzword programmers who are incapable of generating an original thought.
This is a great mistake, and one that I learned NOT to make many years ago. I will only implement those ideas that have proven, in my personal experience, to be beneficial or have merit. This has led me to ignore a large number of ideas which have been approved by the Paradigm Police/OO Taliban and to follow instead an older, more mature set of ideas which, due to their unfashionable nature, have been described as "unorthodox" at best or "heretical" at worst.
When it comes implementing a solution, and there is choice between two alternatives - one simple, one complex - I will always follow the KISS principle and stick with the simplest solution. This follows on from the following statement made by C.A.R. Hoare:
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.
I have expanded this statement into the following:
Any idiot can write complex code than only a genius can understand. A true genius can write simple code that any idiot can understand.
The mark of genius is to achieve complex things in a simple manner, not to achieve simple things in a complex manner.
Despite the fact that I have been using my heretical approach for over ten years there are people out there who dismiss it out of hand as being rubbish, not because they have examined it and compared it with their own implementation, but because they have been told that I do not follow the "right" OO standards therefore everything I do must surely be wrong. Being wrong it must be bad, and being bad it must exhibit the characteristics of bad software such as being unreadable, difficult to maintain, take longer to write, longer to debug, et cetera, et cetera. This is a premise which leads to a conclusion. I shall now demonstrate that the conclusion is totally wrong which therefore proves that the original premise must also be wrong.
The fundamental point of my technique is that it enables me to create basic components to maintain the contents of database tables WITHOUT WRITING ANY CODE WHATSOEVER. This is in direct contrast to the volumes of code that need to be written just to comply with those academic yet impractical principles that are so loved by the Paradigm Police and the OO Taliban.
Let me prove this with an example and a challenge. Every database application consists of one or more databases containing one or more tables, and each table has its own set of columns, keys and indexes. Tables may be related to other tables in what is known as relationships where one table (the child) contains a reference (known as a foreign key) to another table (the parent). Relationships will also require some form of referential integrity.
In order to maintain the contents of a database table the application software will have a number of user transactions (sometimes known as "tasks" or "use cases") each of which will perform one or more actions on one or more tables. In an online system each task will communicate with the user by means of a User Interface (UI), also known as a form or screen, on a client device such as a PC, tablet or smart phone. Each transaction will have a name which is meaningful to the user, such as "Create Account", "View Account" or "Pay Invoice", and these names will appear on some sort of menu system which enables the user to quickly activate the relevant task for a specific business operation. In my own enterprise application, for example, I have over 350 database tables and over 2,500 tasks.
Suppose I add a new table to my application database and then want to implement a standard family of CRUD forms as shown in Figure 2:
Figure 2 - A typical Family of Forms
Each of the six components above will perform a single function on that single table. The LIST and ENQUIRE tasks will perform a READ operation on that table, the ADD will perform a CREATE, the UPDATE will perform a READ and an UPDATE, the DELETE will perform a READ and a DELETE, while the SEARCH will supply data which is used by the LIST1 task to filter its results. Note that each task in that diagram is a hyperlink which will take you to a full description of their structure and behaviour.
As well as constructing the six new components they should also appear as options in the application so that they can immediately be run. The parent LIST task should be made available on a menu screen, while its associated child tasks should only be available as options within the parent task.
Here is the challenge: using your favourite framework how much code would you have to write to implement that family of forms and how long would it take? If you have to write ANY code, or it takes longer than FIVE minutes then I'm afraid that you can back up your bags and go home as you have failed!
If you cannot do what I do then you have no right to tell me that I am wrong. If your standards don't allow you to produce software of the same quality and at the same speed as mine then I'm afraid that it's your standards which are crap, not mine.
Using my so-called heretical and inferior techniques I can create that family of forms in less than five minutes and without writing a single line of code - no PHP, no HTML and no SQL. How? I can achieve this level of productivity simply because my approach is pragmatic instead of dogmatic, which means that I don't follow artificial rules and hope the result is satisfactory. Instead I concentrate on producing the best result possible and ignore any so-called "rule" which gets in the way. Below is a side-by-side comparison of the "right" way (i.e. approved by the paradigm police), and my not-right-therefore-it-must-be-wrong way (which is also documented in Object Oriented Database Programming):
|the "approved" way||the Tony Marston way|
|1||In the world of Object Oriented Design the software is king, and the database is nothing but an "implementation detail".||No. Refer to OO theory is king and the database is just a implementation detail for details.|
|2||Everything is an object.||No. Refer to Everything is an object for details.|
|3||Use Object Oriented Design (OOD) with its "is-a" and "has-a" rules.||No. Ignore OOD completely as adequate results can be obtained directly from the Database Design. Every entity in the business/domain layer "is-a" database table, and each table "has" nothing but its own set of columns. Each of these columns is represented as a simple value, not an object. Each table has its own set of business rules, so the table's class is the obvious place to define all of those rules.
I have seen too many examples where someone says "We need a CUSTOMER table, but as each customer "is-a" person we must start with a PERSON class and inherit from this to create a CUSTOMER class". There is no such concept in database design, so to include it in the software would be a pointless complication.
Further details are also available in the following:
|4||Use object composition or object aggregation to group several database tables into a single class.||No. Refer to you must use Object Aggregation/Composition for details.|
|5||Favour composition over inheritance||No. Refer to You must favour Composition over Inheritance for details.|
|6||Use associations to define a relationship between classes of objects that allows one object instance to cause another to perform an action on its behalf.||No. Refer to You must use Associations to define relationships between classes for details.|
|7||Design complex class hierarchies, where one concrete class is extended to form another concrete class. Such hierarchies can be five or more levels deep.||No. Each concrete class "is-a" database table and can therefore inherit from a single abstract table class, thus producing a class hierarchy which is only one level deep. I never extend a concrete table class into another concrete class, which means that I never have the problems which other people have encountered.|
|8||Identify all the methods/operations which can be performed either on or by the entity which the table represents.||No. The software is NOT interacting with any external entities, only tables in a database, so only those operations which can be performed on a database table are relevant. These operations are Create, Read, Update and Delete, and are generic enough to be defined in the abstract table class which is inherited by every Model class.
Once I have designed a properly normalised database all I need do is create a separate class for each table. This is generated by the framework rather than being created by hand.
Each use case is then implemented as a different user transaction which links one of the Transaction Patterns to a Model. The Controller which implements each of these patterns will only use those generic methods which were inherited from the abstract table class.
|9||For use cases such as "Create Account", "View Account" or "Pay Invoice" you must create methods with the same names.||No. Refer to You must create a separate method for each use case for details.|
|10||Create a separate property for each column in the table.||No. Refer to You must have a separate class property for each table column for details.|
|11||Create a getter and setter for each column in the table.||No. Refer to You must access each class property using getters and setters for details.|
|12||You must use the constructor to populate an object with data.||No. Refer to You must use the constructor to populate an object with valid data for details.|
|13||Data must be validated before it is put into the Model as the Model is not allowed to contain invalid data.||No. Refer to You must validate all data before it is put into the Model for details.|
|14||You must validate each value within its setter.||No. Refer to You must validate a value within its setter for details.|
|15||Each object can only deal with a single row from a database table.||No. Refer to You must access only one database row within each object for details.|
|16||To implement MVC a Controller can only access a single Model.||No. Refer to a Controller can only speak to one Model for details.|
|17||To implement MVC a Model can only be accessed by a single Controller.||No. Refer to a Model can only have one Controller for details.|
|18||Build each Model class by hand to contain all the properties and methods which have been identified.||No. Refer to You must construct each Model class by hand for details.|
|19||Hand craft a separate controller for each object in order to call all its methods.||No. Refer to You must construct each Controller by hand for details.|
|20||Hand craft a separate view for each object in order to display its data in whatever formats may be required.||No. Refer to You must construct each View by hand for details.|
|21||Hand craft a separate script for each task (user transaction)||No. To generate a new task or a family of tasks use the Data Dictionary to select a table, link it with a Transaction Pattern, then press a button to generate a new component script for each task. Note that this procedure will automatically add the relevant entries to the MENU database.|
|22||Create a separate Data Access Object for each table in the database which can construct all the necessary queries for that table.||No. I do not have a separate DAO class for each table, only for each supported DBMS - currently MySQL, PostgreSQL, Oracle and SQL Server. This handles all communication with the physical database. The abstract table class, which is inherited by every Model class, contains all the relevant methods to instantiate and pass control to the relevant DAO as and when necessary.|
|23||Use an Object Relational Mapper (ORM) to handle all communication with the database.||Definitely not. Refer to Object Relational Mappers for details.|
|24||Create a variety of finder methods to access records in each database table.||No. The SQL language does not have such things. Data can be filtered by using the general-purpose WHERE clause in a SELECT statement. This is a simple string which can contain an infinite variety of keywords and values. The idea of creating a separate method for each possible combination is not something that would ever occur to me, not even in a month of Sundays.|
|25||Use as many Design Patterns as possible, especially in weird combinations, just to prove how clever you are.||No. You should not make a list of 'cool' patterns and then try to implement them, you should design and code incrementally, and only refactor to patterns as you understand more of the problem. A design pattern should only be applied when the flexibility it affords is actually needed. Refer to Design Patterns - a personal perspective for details.
IMO design patterns are the wrong level of abstraction. I get much more reusability from Transaction Patterns, as described in Design Patterns are dead! Long live Transaction Patterns!.
|26||Implement all the SOLID principles until they can be implemented no more, just to prove how clever you are.||No. These principles were badly written as they do not provide clear, concise, accurate and unambiguous definitions. This has led to them being redefined and reinterpreted so many times that the original idea has been totally lost. It is therefore impossible to follow one of these interpretations without upsetting the supporters of the others. This is a lose-lose situation as whatever you do will be perceived as wrong by someone somewhere.
For a more detailed critique please refer to Not-so-SOLID OO Principles.
|27||Use exceptions for all validation errors.||No. Refer to Exceptions for details.|
|28||You must use object interfaces.||No. Refer to Object Interfaces for details.|
|29||You must use the visibility options.||No. Refer to Visibility for details.|
|30||You must keep up with all the latest features in the language.||No. I designed and built my PHP framework using PHP 4, and as this provided full support for encapsulation, inheritance and polymorphism I managed to achieve the objectives of OOP by creating more reusable code and reducing code maintenance. Although various new features have been added to PHP 5 in order to support what others seem to think are "essential" in OOP, I ignore these optional extras because the time spent in adding them to my code would be greater than any value that would be added. Anyone who understands the concept of Cost-Benefit Analysis or Return on Investment will understand my reasoning.|
|31||You must hide the fact that your software is communicating with a database||No. Refer to Object Oriented Database Programming for details|
|32||You must use a Front Controller||No. Refer to Why don't you use a Front Controller? for details.|
|33||You must not use global variables||No. Refer to Your code uses Global Variables for details.|
|34||You must not use singletons||No. Refer to Your code uses Singletons for details.|
|35||Each object must have a unique identity||No. When I create an object it is put into a variable which has a name, and that name is more than sufficient. Each object is used to manipulate data, and after each request has processed it simply dies, and all in-memory objects used in that request die with it. Objects do not persist after the request dies, but any data which was placed in a database does. Objects are not stored in a database, only tables and columns. Object Oriented databases are an idea that has never materialised, so the notion of storing objects in a database is a pipe dream. For each new request any persisted data can be pulled from the database and placed in a brand new object, so the need for object identity does not exist. Each record in the database has its own identity (called a primary key) and I have never found a use for anything other than this.|
The above list identifies all the steps that you DON'T have to take in order to write effective software. By ignoring the "approved" methodology and following one which, based on the KISS principle, is as simple as possible and which bypasses unnecessary complexity, I have created a framework which takes all the drudgery out of writing database applications. With it I can create a simple family of forms without writing a single line of code, but which covers all the basic operations and which has access to all the features provided by the framework. Only a small amount of code is actually generated by the framework:
Each of these scripts is so small for the simple reason that all the "heavy lifting" is done within the framework by volumes of pre-written and reusable code.
If the stated purpose of OOP is to provide ways of increasing the volume of reusable code, and I have done just that, how can you possibly tell me that my methods are wrong? Unless you are capable of producing comparable amounts of reusable code I would suggest that it is your methods which need to be questioned, not mine.
Some people might think that due to the simplicity of the above tasks that my framework is only capable of dealing with simple CRUD screens. Think again. The above example uses only 6 of the available 40+ items in my library of Transaction Patterns, so there is a pattern for every possibility that I have encountered in the 30+ years that I have been designing and building enterprise applications. In addition to the basic functionality that each pattern provides, any of this behaviour can be extended or replaced by putting appropriate code in any of the available customisable methods.
Building database applications with my RADICORE framework is a breeze, provided that you start with a database design. It does not matter if the database design changes during the development process as such changes can be dealt with quite easily by the framework. The number of database tables is largely irrelevant, as is the number of tasks required to deal with those tables. Anyone who builds enterprise applications should be aware of Len Silverston's Data Model Resource Book in which he provides a library of universal data models for all enterprises. In 2007 I started building an enterprise application based on several of his database designs - PRODUCT, PARTY, ORDER, INVENTORY, INVOICE and SHIPMENT - and in a short space of time I had a working application which I could demonstrate to my client. By "short space of time" I mean six man-months, which equates to an average of one month per database. Can your framework match that? If not then please stop telling me that my methods are wrong when clearly my results are superior.
This enterprise application is still going strong, and to date it has 369 tables, 718 relationships, and 2,900 user transactions. It is being sold to multi-national organisations all over the world through my business partner Geoprise Technologies.
Throughout the exchange of views between myself and Hall of Famer in What's Wrong With Object-Oriented Programming? and also MVC vs. OOP he has continuously proved that he cannot understand simple concepts and keeps on insisting that any opinion which is different from his is automatically wrong. He is operating under the belief that if he keeps repeating the same lies over and over again that eventually they will be perceived as the truth. By constantly casting aspersions on my competency as a programmer, the quality of my work, and even the competency of my customers, he is guilty of gaslighting, but his words will never convince me that I am wrong for the simple reason that my code works, and any engineer, software or otherwise, will tell you that something that works cannot be wrong. Not only do my methods work, they actually produce results which are are more cost-effective than his simply because I can produce working components at a much faster rate that he can, and everyone knows that faster means cheaper. My framework also provides many features which are automatically available to every component "out of the box" without the need for additional coding.
In any engineering discipline there are laws that must be followed otherwise your project will fail. There are other things called "rules" which have no effect on the success or failure of the project, but which are added to please the bureaucrats so that they can tick boxes on pieces of paper. For example, when building an aeroplane you must obey the laws of aerodynamics otherwise your plane will not get off the ground. The fact that a bureaucrat has a piece of paper on which all the boxes have been ticked will be irrelevant. For example, there may be rules which state how an aircraft should be built, what materials should be used, what shape it should be, et cetera, but these are far less important than the laws of aerodynamics. You may build a plane that ticks all the bureaucrat's boxes, but if it doesn't fly it is a failure.
If an innovator comes along with a different approach which allows aircraft to be built faster and cheaper by utilising different techniques, different materials and/or different shapes, the only important factor in the eyes of the paying customers is "Does it fly?" If it happens to be cheaper than his competitors then the innovator will take business away from them. In such circumstances it would be pretty pointless for the competitors to complain that the innovator is breaking the rules. The definition of "innovate" is
Make changes in something established, especially by introducing new methods, ideas, or products. Innovation requires change, and this sometimes means throwing out the current rule book and starting again with a new set of rules.
If I break an engineering law and something bad happens, then the fault is mine. My plane won't fly, my boat won't float, my bridge will fall down. If I break a bureaucratic rule and nothing bad happens, then that rule has no practical purpose and its existence can be questioned. On the other hand the breaking of a rule may result in something pleasant, such as better performance, quicker delivery or lower costs. The fact that a bureaucrat is upset because he can no longer tick that box on his sheet of paper becomes irrelevant.
There are very few "laws" in the world of software engineering. Personally I can see only three:
When writing database applications for the web I am constrained by the following:
When it comes to programming style there is no "one size fits all" approach such as that being demanded by Hall of Famer. The only "rule" that I have followed in all the languages that I have used is based on the following statement from Abelson and Sussman in their book Structure and Interpretation of Computer Programs which was first published in 1985:
Programs must be written for people to read, and only incidentally for machines to execute.
This requires the use of meaningful data names, meaningful procedure names, a structure that can be shown in a diagram, and logic that can be easily understood. Along the way I have adopted other principles such as KISS, DRY and YAGNI, but anything else I see as a passing fancy and not a universal law that must be obeyed without question. In order to write effective software I have to obey the laws of software engineering, but standards are not laws, they are guidelines. As a pragmatic programmer I will only follow those guidelines which have proved their worth in my 30+ years of programming experience. As Petri Kainulainen says in his article We Should Not Make (or Enforce) Decisions We Cannot Justify I have the right to question any rule in order to evaluate its actual worth. Any guideline which stands in my way will be brushed aside, and provided that my paying customers are impressed with the result I couldn't care less about upsetting the delicate sensibilities of some petty bureaucrat.
Fundamental - forming the base, from which everything else develops
Evolve - to gradually change over time
In this post I made the following statement:
OOP consists of nothing more than encapsulation, inheritance and polymorphism, and everything else is an optional extra.
He replied with the following
those are NOT add-on definitions of OOP, they are fundamental and universally agreed concepts.
I pointed out to him that the fundamental or original definition of OO was made by Alan Kay who invented the term where he stated that OO consists of nothing more than encapsulation, inheritance and polymorphism, so anything added after that is an optional extra. There are now many different languages which implement OO theory in different ways, into which different people have added in their own ideas of how OO could be "improved", but all these ideas were later additions and not included in the original definition of OO.
Logic - program code which performs a function
Information - data which is processed by code
In this post he made this statement:
When a class has 9000 lines, it is guaranteed to have more than one responsibility
I tried to explain that my framework had been separated into an adequate number of components by virtue of the fact that it implemented a combination of both the 3-Tier Architecture and Model-View-Controller design pattern. As my so-called "God" class contains logic which is restricted to operations performed in or by the Model, and contains no logic which rightly belongs in a Controller, View or DAO, it does not break SRP at all. I asked him to point out any logic in my abstract class which should be in one of the other components, and in this post he answered with:
I stopped reading once I found file uploading logic/responsibility in that class
He made the same accusation in this post, to which I replied with:
No table class performs any file uploading as that is done within the Controller. It merely has methods which supply the destination directory and file name, plus a post-upload method which allows for additional business rules to be specified after the upload has been completed.
In this post he continued his argument with the following:
Even though the actual file uploading is done in your controller class, your god class does contain File uploading logic. Its not just a courier of information, it actually processes this information.
Hall of Famer clearly does not understand the difference between logic and information, that having a method in a Model which passes information/data back to the Controller which performs the file upload is not the same as having logic/code within the Model that performs the file upload. The Model passes the data to the Controller, and it is only the Controller which contains the logic/code which processes this data.
Hall of Famer seems to have the notion that just because a feature exists in the language then it should be used, or if a programming principle has been created by a supposed "mastermind" then it should be followed. Intelligent people know different. Unlike a mountain climber whose justification for climbing a particular mountain is "Because it is there!", in computer programming you only use a particular feature of the language, a particular function, or particular syntax when it is helps you achieve the objectives of the program or library which you are writing. I have used many languages in my long career, and I have rarely used more than 50% of the features in any of those languages. Why not? Because I had no use for them. Just because they are useless to me does not mean that they are also useless to everybody else.
Although I could use a new feature that has been added to the language it is not the same as should. Sometimes a feature is added to provide coverage for a topic that was not covered previously, but sometimes it is added just to provide a different way of doing something that can already be done. Each time a see a new feature in the language I ask myself some simple questions:
If I don't have the problem that a feature was meant to solve then I have no use for that feature. This includes namespaces.
If the cost of changing existing code to do the same thing, but in a different way, is greater than the benefits that would be provided, then use of that feature cannot be justified. I would put autoloaders and short array syntax in this category.
If a feature was designed to solve a problem in the language that no longer exists, such as object interfaces, then that feature is dead and should be removed. Object interfaces did not exist in PHP 4 as they were not necessary. They were only added in PHP 5 because a vociferous (i.e. loudmouthed) group of developers used the pathetic argument that "interfaces eist in other OO languages, so they should be in PHP as well".
Structured Programming - a programming paradigm aimed at improving the clarity, quality, and development time of a computer program by making extensive use of subroutines, block structures, for and while loops.
Monolithic - A software system is called "monolithic" if it has a monolithic architecture, in which functionally distinguishable aspects (for example data input and output, data processing, error handling, and the user interface) are all interwoven, rather than containing architecturally separate components.
In this post he made the following statement:
Yes it is possible to write well-structured Java and COBOL code, I never said you cannot. However its impossible to write well-structured procedural code. Procedural code can be structured, but only badly structured. Writing procedural Java code, then its badly structured. Writing COBOL code with OO design, then its well structured.
You should see here that he is contradicting himself:
As COBOL is a procedural language then one of those statements must be a lie.
The dictionary definition of "extreme" contains the following descriptions:
The dictionary definition of "moderate" contains the following descriptions:
Despite me telling him over and over again that I have followed a reasonable and moderate interpretation of SRP by implementing a combination of the 3-Tier Architecture and Model-View-Controller design patterns, which, like Robert C Martin's description of SRP, identify only three areas of logic which should be separated, he continued his argument in this post in which he said:
you fail to understand that SRP applies to not only the tiers/layers, but also to the subcomponents inside each tier/layer.
The descriptions of the 3-Tier Architecture, MVC and SRP only identify three areas of logic each. There is absolutely no mention of any need to go any further with the process of separation.
He followed up with this post:
There may be only 3-4 tier/layer, but tier/layer = responsibility only in extremely simple applications. So you see? My interpretation is not extreme, your interpretation is. In fact, when you have MVC structure, this simple assumption already fails, since both controller and view belong to UI layer and they are two different responsibilities. In this case, the UI logic already contains two responsibilities. And the fact that Robert C Martin didn't say these areas can be broken down further, doesn't mean they cannot or shouldn't.
Here he is saying that if a principle or pattern only identifies three areas of logic that can be separated, then only dealing with those three areas is "extreme" while subdividing each of those areas into smaller units is nothing more than "moderate".
He he also saying that just because a definition does not explicitly say that something should not be done does not mean that it should not be done, that unless it explicitly says that you should not do it then by implication it means that you should do it. This is twisted logic. Every instruction manual ever written has followed the same simple pattern - it identifies only those things which are relevant, which may include a list of Do's and Dont's. Anything that it does not mention is therefore not relevant, so if you do something which has not been identified as relevant then you should be prepared to accept the consequences and not blame the instruction manual.
The dictionary definition of "dogmatic" contains the following descriptions:
The dictionary definition of "pragmatic" contains the following descriptions:
In this post he made the following statement:
I am not a dogmatist, I am a pragmatist. I am flexible and I accept many ideas and opinions. The reason why I don't accept your is that, your idea really is complete bullshit
In this post I replied with:
I'm afraid you have got those two the wrong way round. A dogmatist spends too much time following the rules without any regard or the result. A pragmatist concentrates on the result and ignores any rules which get in the way. You are the one who insists on extreme interpretations of the rules, and I am the one who ignores the rules based on those absurd interpretations.
In this post Hall of Famer said the following:
you are the dogmatist, you spent too much time following your own set of rules so you ignore the valid points and standards from the elites/masterminds
Here he is redefining those words to mean something completely different:
In this post he made the following statement:
MVC in general is an incomplete architectural pattern that only distinguishes three components in your application, but not all. In a smaller and simpler application, sure M, V and C themselves alone are sufficient. In more complex applications, you need more components, such as DAO, Service Objects, Helper Objects, etc. You call yourself a pragmatist, not a dogmatist, then shouldn't you strip yourself out of the MVC dogma?
Here he appears to be saying that if you follow the description of a pattern as it is written then you are a dogmatist, whereas if you go beyond what is written an apply a more extreme interpretation then you are a pragmatist.
How is it possible to have a meaningful discussion with someone who keeps redefining words to suit their own point of view?
The internet is a wonderful place to have a meaningful exchange of ideas with intelligent people who are willing to discusss different ideas on their merits. It is a pity that it is also inhabited by bigotted narrow-minded zealots and dogmatists like Hall of Famer who are incapable of accepting any views which they consider to be unorthodox, unapproved and therefore heretical. He seems to think that simply stating that my opinion is wrong is the same as proving that it is wrong. Instead of responding to my arguments with counter-arguments which are based on logic he resorts to insults such as the following:
Your viewpoints are mostly incorrect, inferior and confused.
I find your incompetency as a programmer amusing.
I am not insulting you or your work, you are an incompetent programmer and your work is utter trash.
If his views are being taught as the only way to do "proper" OOP then God help the software industry.
When my critics such as Hall of Famer make such statements as
When a class has 9000 lines, it is guaranteed to have more than one responsibility they are basing this assumption on size instead of substance. There is no limit to the size of a class, either abstract or concrete. The only deciding factor is called cohesion, and that requires a modicum of intelligence that goes beyond the ability to count higher than ten without having to take your shoes and socks off. My architecture, as shown in Figure 1, is based on a reasonable and moderate interpretation of SRP where the logic has been separated into a Model, View, Controller and DAO. My so-called GOD class is an abstract class which is inherited by every Model, and which contains all the methods necessary to handle the communication from the Controller or to the DAO. There is absolutely no program logic in the Model which belongs in the Controller, View or DAO, so any accusation that it breaks SRP is completely bogus. Unless of course, you have a perverted and extreme interpretation of SRP, which I do not.
When my critics make such statements as
You are not following the standards, therefore your code must be crap they are assuming that it is only their interpretation of the standards that leads to the production of good software. The simple truth is that my interpretation of the standards, and the way in which I have implemented them, has allowed me to create enterprise applications in a more cost-effective manner than any of my rivals. This is because I have fulfilled the objective of OOP and created more reusable components than I could with any of my previous non-OO languages. Using my open-source RADICORE framework I can create initial working user transactions without having to write a single line of code, a feat which is impossible by those who follow the "right" standards. My methods work, so they cannot be wrong. My methods produce superior results, so they cannot be crap. Only a methodology which produces inferior results can be regarded as crap, so I would suggest that Hall of Famer looks closer to home if he wants to find crap.
As you can see from the above list I do not follow a significant number of the techniques/principles/rules which have been approved by others in the programming community. The primary reason for this is because these rules simply did not exist when I made the switch to an OO-capable language after working for several decades in a variety of other languages. What resources I found on the internet made no mention of them at all. All I found were a few small articles which provided basic examples of how to implement the principles of OOP which were described as follows:
Object Oriented Programming is programming which is oriented around objects, thus taking advantage of Encapsulation, Polymorphism, and Inheritance to increase code reuse and decrease code maintenance.
This told me that the only difference between an OO language and a non-OO language is that one supports Encapsulation, Inheritance and Polymorphism while the other does not. Both execute code in a linear fashion, and both allow code to be grouped into functions. The only difference is that with OOP the functions are called "methods" and related methods can be grouped into "classes" which can then be instantiated into "objects". Code can be shared using inheritance, and the ability for different classes to share the same method signature provides a mechanism called polymorphism. By using these three capabilities in the language I should be able to produce a higher volume of reusable code, thus shortening development times and lowering costs. The more reusable code you have the less code you have to write, and as the reusable code has already been used and debugged it also decreases the maintenance effort. As I only build database applications I created a framework to assist in the development and running of these applications, and I based this on similar frameworks which I had created in two of my previous languages. As database applications are written specifically to put data into and get data out of a database, with a bit of processing in between, I built my framework specifically to assist in this type of processing, so it makes no attempt to disguise the fact that it is communicating with a relational database.
After producing code which worked I decided to publish articles describing my techniques on the internet in order to share my results with others and hopefully add to the pool of knowledge. Imagine my surprise when I was told that everything I was doing was wrong!
I refused to accept that I was wrong for the simple reason that my techniques worked, and anything which works cannot be wrong. I was incorporating encapsulation, inheritance and polymorphism into my code and achieving the objective of producing more reusable code, so I could see no justification for their complaints. Having more reusable code provided me with the following:
Some of the arguments I was given to persuade me to change my unorthodox ways were devoid of any substance.
Real OO programmers don't do it that way or
That is not how it is supposed to be done simply cannot be justified as they are devoid of details. I want to know what the problem is. To be informed that there is some sort of a problem is not good enough.
Some of the explanations I was given simply did not add up. Take the following statement, for example:
Having a separate class for each database table is not good OO. Abstract concepts are classes, their instances are objects. IMO The table 'cars' is not an abstract concept but an object in the world.
Surely the concept of a database table is abstract, while the 'cars' table is a concrete instance of that abstraction. Then why is it wrong to have an abstract table class which is inherited by every concrete table class? Any code which is common to every database table can therefore be defined in the abstract class and shared by every concrete class. How can this be the wrong use of inheritance?
Some of the reasons were to get round a problem which I did not have. For example, the rule "favour composition over inheritance" was only invented for those retards who made a complete dog's dinner of inheritance and created deep inheritance hierarchies by inheriting from one concrete class to create a completely different concrete class. I never do this, which means that I don't have that problem, which also means that I have no use for that solution.
Some of the explanations are expressed using terminology which is designed to show the cleverness of the author but which is lost on us mere mortals. For example, take the following statement:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
I have been doing OOP for over 10 years, and I have learned to look up the meaning of words in wikipedia. The term "abstraction" has two possible meanings - a verb and a noun, where the latter is the result of performing the former. An abstraction is a concept from which concrete examples can be created or instantiated, which leads me to believe that in computer science the act of performing an abstraction should result in the creation of an abstract class from which a number of concrete classes can be instantiated. The description above is actually used for the Dependency Inversion Principle (DIP), but nowhere does it mention either of the critical aspects of "dependency" or "injection" which is what that principle is all about. Nowhere in the implementation of that principle does it mention the need for any abstract classes, so that description is therefore both meaningless and misleading.
Some principles and design patterns are designed for a specific set of circumstances, and unless those circumstances exist in your software then implementing that particular principle or pattern could end up by doing more harm than good. Adding in code to deal with a problem which you don't currently have and are unlikely to have is nothing more than a waste of time. See YAGNI for details.
Some principles are overused by some people simply because they don't know when to stop. For example, I use SRP up to a point, but I cease and desist when it starts to break encapsulation and destroy cohesion. Other less intelligent souls keep extracting until there's nothing left to extract, but they fail to see that the result is a huge set of anemic micro-classes which resemble ravioli code and which can bloat call stacks and make navigation through the code for maintenance purposes much more difficult.
The purpose of a software developer is to develop cost-effective software for the paying customer, not to impress other developers with the cleverness or "purity" of their code. Producing software in a timely manner which is good enough for the customer should be more important than wasting time in attempting to produce software which is perfect in the eyes of a few developers. The concept of good enough is easier to define and easier to deliver than perfect, so that is where I prefer to concentrate my efforts.
When people like Hall of Famer tell me that because I don't follow "the standards" that my code must be crap I just have to shake my head at their naivete. I am not breaking "the" standards as there is no such thing as a single set of standards which is universally followed by all programmers. Such a document has NEVER been published. There is no "one size fits all" when it comes to programming standards as different groups of programmers will use whatever suits them best. Some programmers adopt a pragmatic and moderate approach while others, those that I refer to as the Paradigm Police or the OO Taliban, lean more towards the dogmatic and the extreme. Besides, the only real rule in programming is to write software that works and which can be understood and maintained by others. Everything else is just icing on the cake, a nice-to-have, an optional extra. They are guidelines, not rules. Following them will not guarantee that your software will succeed any more than not following them will guarantee that your software will fail. It is not which principles or rules you follow but how you follow them which is important. You need to know when the circumstances are appropriate and benefits can be gained by following a principle, but just as importantly when the circumstances are not appropriate in which case the benefits could be zero or even negative.
Here endeth the lesson. Don't applaud, just throw money.
Here are some other heretical articles I have written on the topic of OOP:
© Tony Marston
6th December 2016
|01 Feb 2017||Increased the number of topic in My technique is different, unorthodox, and unapproved from 16 to 35.
Added The Faulty Logic of "Hall of Famer"