Tony Marston's Blog About software development, PHP and OOP

Response to personal attack on reddit.com

Posted on 31st January 2020 by Tony Marston
Introduction
- The basic principles of Object Oriented Programming (OOP)
- The true meaning of the Single Responsibility Principle (SRP)
- The wrong meaning of SRP
- Programming is a balancing act
The class does too much
This is a GOD class
if ... else structures
method_exists
endif
Calls to is_True()
This is PHP 4 code
This class is utterly terrible
A message for Robert C. Martin
It's not the Rules which count, it's the Results
Comments from the not-so-clueless
References
Comments

Introduction

In March 2017 one of my clueless critics posted an article on reddit which criticised the abstract table class which exists in my RADICORE framework. I refer to him and the other respondents to this thread as being "clueless" for the simple reason that they are all exhibiting the traits of clueless newbies as they don't fully understand the principles of OOP and how to apply them in an efficient and effective manner. This therefore makes them nothing more than cargo cult programmers in that they do not understand the words they read and therefore have difficulty in putting them into practice. By continually broadcasting their ill-informed ideas they become nothing more than echo chambers for outdated ideas.

The main idea behind the reddit post is that the code sample is so large that it surely must be breaking the Single Responsibility Principle (SRP). The contents of the post clearly indicate to me that these clueless newbies do not have a proper understanding of either the principles of OOP or the true meaning of SRP.

I have resisted till now the impulse to reply to this post as I felt that it would be a complete waste of time, like casting pearls before swine, but as the army of swine (aka "clueless newbies") is apparently growing at a faster rate I felt that another dose of pearls (aka "wisdom") is about due.

WARNING - this article contains opinions that those of a delicate nature might find offensive, so it may be advisable to have a dose of smelling salts at hand. You have been warned!

The basic principles of Object Oriented Programming (OOP)

As stated in What OOP is a language or technique is object-oriented if and only if it directly supports:

  1. Abstraction/Encapsulation - providing some form of classes and objects.
  2. Inheritance - providing the ability to build new abstractions out of existing ones.
  3. Runtime polymorphism - providing some form of runtime binding.

Everything else I regard as an optional extra which means that it is my choice whether I use them or not, and I can safely ignore any criticism from those who complain that I am not making the same choices as them.

The first lesson that clueless newbie programmers need to learn is what type of objects to create. In his article How to write testable code the author identifies three distinct categories of object:

  1. Value objects - an immutable object whose responsibility is mainly holding state but may have some behavior. Examples of Value Objects might be Color, Temperature, Price and Size.
  2. Entities - an object whose job is to hold state and associated behavior. Examples of this might be Account, Product or User.
  3. Services - an object which performs an operation. It encapsulates an activity but has no encapsulated state (that is, it is stateless). Examples of Services could include a parser, an authenticator, a validator or a transformer (such as transforming raw data into XML or HTML).

The PHP language does not have value objects, so I shall ignore them.

It would be advisable to avoid the temptation to create Anemic Domain Models which contain data but no processing. This goes against the whole idea of OO which is to create objects which contain both data and processing.

The true meaning of the Single Responsibility Principle (SRP)

The problem with this principle is that the original definition was badly written which left it open to enormous amounts of interpretation and thus mis-interpretation. This definition was:

The Single Responsibility Principle (SRP) states that each software module should have one and only one reason to change.

But what exactly is a reason to change? Different people came up with different interpretations which led to different implementations, and several clueless newbies came up with the idea that if a module did "too much" then it must be handling more than one responsibility and therefore should be split into smaller modules. Note that while "too much" can be interpreted as "having more than one responsibility" the clueless newbies out there prefer the simpler interpretation of "a class with more than N methods" or "a method with more than N lines of code" where "N" is a totally arbitrary number. This indicates to me that these clueless newbies have the ability to count but not the ability to think. I have seen the number 10 quoted more often than not for this theoretical limit, which leads me to believe that these clueless newbies cannot yet count to higher than 10 without taking their shoes and socks off.

In two later articles the author of this principle, Robert C. Martin (Uncle Bob), came up with some better and more meaningful descriptions. In Test Induced Design Damage? he wrote:

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 separate is good design.
In The Single Responsibility Principle he wrote:

This is the reason we do not put SQL in JSPs. This is the reason we do not generate HTML in the modules that compute results. This is the reason that business rules should not know the database schema. This is the reason we separate concerns.

What Uncle Bob is describing here is the 3-Tier Architecture which has three separate layers - the Presentation layer, the Business layer and the Data Access layer - and which I have implemented in my framework. This architecture also has its own set of rules which I have followed to the letter. So if I have split my application into the three separate layers which were identified by Uncle Bob then who are you to tell me that I am wrong?

Later on in the same article Uncle Bob also says the following:

Another wording for the Single Responsibility Principle is:

Gather together the things that change for the same reasons. Separate those things that change for different reasons.
If you think about this you'll realize that this is just another way to define cohesion and coupling. We want to increase the cohesion between things that change for the same reasons, and we want to decrease the coupling between those things that change for different reasons.

If you bother to read what Uncle Bob wrote in his articles you should see that he talks about areas of logic which are responsible for (or concerned with) different parts of a program. Nowhere does he state that each part should be limited in size, only in what it does. This totally destroys the argument that my abstract class is breaking SRP simply because of the amount of code it contains. Anyone who has ever written a large enterprise application will be able to tell you that there is no limit to the lines of code that a module may contain, only that it should contain only one type of logic - presentation (UI) logic, business logic or data access (SQL) logic.

The idea of breaking an application down into three separate layers has also been discussed by Martin Fowler in his article Presentation-Domain-Data Layering.

While it is possible to take one of those modules and to break it down further, care should be taken it not going too far otherwise you will create a fragmented system with low cohesion which becomes more difficult to maintain. An example which I have employed is where I have taken my Presentation layer module and split it into two parts giving me a Controller and a View. Note that the View can come in different flavours - one for HTML output, one for CSV and another for PDF. This produces the structure shown in Figure 1:

Figure 1 - The MVC and 3-Tier architectures combined

infrastructure-faq-05 (5K)

The four components used in the RADICORE framework have the following object types as identified previously in The basic principles of OOP:

  1. Model - this is an entity. One of these is created for each entity in the application and holds all the business rules for that entity.
  2. View - this is a service, a reusable component which is provided by the framework.
  3. Controller - this is a service, a reusable component which is provided by the framework.
  4. Data Access Object - this is a service, a reusable component which is provided by the framework.

Note that all domain knowledge is kept entirely with the Model classes for each domain/subsystem, which means that the Controllers, Views and DAOs are completely domain-agnostic. This means that they are not tied to any particular domain and can therefore be used with any domain.

As the Controllers, Views and DAOs are provided by the framework the only classes which the application developer needs to create are the Models. An enterprise application does not communicate with objects in the real world, it only communicates with objects in a database, and every database developer will tell you that each object in a database is a table. Each table follows a standard pattern in that it has a unique name, a structure which is comprised of a set of columns each of which has a name and a data type which is taken from a fixed list, a primary key, optional additional candidate keys, any number of one-to-many relationships with other tables either as the "one" or the "many". Regardless of what data a table holds there are only four operations which can be performed on it - Create, Read, Update and Delete (CRUD). Because each and every table follows a standard pattern it is therefore possible to create an abstract table class which contains all standard processing which can then be inherited by each concrete table class which provides the specific details for a particular table. Those of you you have gone beyond the stage of being clueless newbies should recognise this as being an implementation of the Template Method Pattern where invariant methods are provided by the abstract class and variable/customisable methods are provided within individual subclasses.

Note here that the abstract table class contains ALL the standard processing which may be performed on ANY table in the database, and as it contains a mixture of invariant methods (which have fixed implementations) and variant/variable methods (which may or may not be overridden in any subclass) this explains why it has a large number of methods. Note also that the Template Method Pattern does not place any sort of limit on the number of methods nor the number of lines of code within each method.

Using this arrangement I achieve vast amounts of polymorphism by virtue of the fact that each method which a Controller can call on a Model is defined within the abstract table class. No Controller is tied to a particular database table so can potentially be used with any table in the database. So if I have 50 Controllers (one for each Transaction Pattern) and 400 database tables this produces 50 x 400 = 20,000 (yes, TWENTY THOUSAND) opportunities for polymorphism. Note that you must have polymorphism before you can implement Dependency Injection, so the more polymorphism you have the more dependencies you can inject. If you cannot produce similar amounts of polymorphism in your framework then I would suggest that your understanding of the principles of OOP and how to implement them to full effect is seriously impaired. Yet you clueless newbies have the audacity to tell me that I don't know what I'm doing!

The wrong meaning of SRP

A clueless newbie called plectid posted these comments:

SPR is about replaceable implementations behind interfaces

Rubbish. SRP has nothing to do with replaceable implementations behind interfaces, it is about splitting code into separate areas of responsibility. Replaceable implementations come from inheritance and polymorphism. Implementing SRP does not guarantee any level of polymorphism.

while the use of interfaces increases cohesion

The use of interfaces does not guarantee high cohesion, that comes from grouping those methods which form part of the same responsibility into the same class. Besides, I don't use interfaces as I get more from abstract classes.

And encapsulation says non-public-interface members should be hidden, and I don't see a single protected/private method.

Encapsulation does NOT mean data hiding, as discussed in Your class methods are too visible as well as A minimalist approach to Object Oriented Programming with PHP.

Programming is a balancing act

There are a lot of principles which need to be taken into consideration when writing a program, and each of them has its own set of costs and benefits.

When to apply a rule or principle, and when to stop applying it are matters for the individual programmer. Writing code requires that several objectives need to be taken into consideration such as ease of development, ease of testing, ease of maintenance, ease of deployment, speed of execution and meeting user expectations. It is very rare to score highly in all these areas as aiming for a high score in one area usually ends up by having an opposite effect in another. This therefore requires a balancing act on the part of the programmer to apply "just enough" of each principle to obtain its benefits, but not "too much" so that it obliterates the benefits of a different principle. This is what prompted me to make the following statement:

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, namely the person who invented that principle, 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 why, in my own framework, I have implemented "just enough" of the principles of encapsulation, inheritance, polymorphism, high cohesion and loose coupling to achieve a huge amount of code reusability. Anything more would not provide any additional benefits, only erode them and apply additional costs, so I regard that "anything more" as being "too much".


The class does too much

That clueless newbie called plectid also posted this comment:

Whoa. A single class responsible for validation, building sql, i18n, pagination, file uploads, and handling custom button clicks. Maintaining? No. I'd stop all other activities and take a month to refactor it into separate, loosely coupled, single responsibility components.

This person clearly does not understand that encapsulation requires that ALL the properties and ALL the methods which relate to a single entity MUST be placed into the same class. Each of these entities is a business entity, therefore forms the Model in the MVC design pattern. None of my Model classes contains any Controller, View or Data Access code, therefore does not violate SRP. The individual complaints I will dismiss separately:

If you write applications where all this functionality is provided in separate classes then you are creating a highly fragmented system with low cohesion which would be a maintenance nightmare. In my design all the business rules for a particular database table are contained within a single class which is dedicated to that table, and as far as I am concerned this adheres to the original definition of encapsulation. When I say "original" I am allowing for the fact that over a period of time certain clueless newbies may decide to offer up some alternative and corrupt definitions.

Someone once asked me to explain what that class does in no more than 20 words, and this is my revised reply:

It is an abstract class that contains methods for any operation that can be performed in any Model class. (that's 19 words)

Note here that it only contains code which is used in Model classes. Controller code only exists in Page Controllers. View code only exists within View objects. Data access code only exists within Data Access objects. If you cannot see that this degree of separation follows precisely what Uncle Bob wrote about then you are blind.

I should also point out that according to the genuine rules of OOP it would actually be impossible for me to break that large class down into a series of smaller classes. Why? Because it is an abstract class which is inherited by every one of my 400 domain classes. Everyone knows that multiple inheritance is not supported in PHP, therefore I cannot inherit from multiple small classes, only a single large one.

This is a GOD class

A clueless newbie called gskema made this statement:

This is literally a GOD class file.

This twat is obviously confusing my code with a GOD object, but as I have already responded to that criticism in another article I shall not repeat myself.

if ... else structures

A clueless newbie called Disgruntled__Goat made this statement:

if (condition) {
    // do nothing
} else {
    doSomething();
}
I don't think there is any way to help someone who after many years of programming, still doesn't understand boolean logic.

Yes, I DO understand boolean logic, but as I obviously have decades more experience than you I have seen that mistakes can be made when dealing with complex conditions which contain several negative statements. In these circumstances I have found it easier to test for an all positive set of conditions and only perform the doSomething() action if the positive condition fails.

method_exists

The same clueless newbie also made this statement:

over 100 occurrences of method_exists
Someone needs to learn what an interface is.

It may surprise you that, unlike some people who have only programmed with PHP for a very short time, I started to write my software in PHP 4 several years before version 5 existed and interfaces were introduced. Consequently I never used interfaces and have no intention of doing so. The method_exists() function predates interfaces by several years and is a perfectly acceptable way of testing for a method before calling it. You may not like it, but your personal preferences are of no concern to me.

Using interfaces does not guarantee that the method exists in that object for one simple reason - you cannot guarantee that the object implemented the interface that contained that method.

I also prefer to use abstract classes instead of interfaces as they allow me to implement the Template Method Pattern which provides the ability for an enormous amount of code reuse which would otherwise not be available.

endif

The same clueless newbie also made this statement:

Also the prevalence of } // if is just cringeworthy.

It may come as a surprise to you but I find the practice of using the "}" character (closing brace) on its own to close a control structure, any control structure, to be a cause of problems rather than a solution. Where you have the "{" character (opening brace) used to open a class, a method and structures such as if, while, do-while, for, foreach and switch which are in a nested structure, then you have to end those nested structures with a series of closing braces without any indication of which brace closes which structure. If the count of these closing braces is incorrect then it will cause an error, but the error message may not be able to indicate which control structure has the incorrect number of braces. This can lead to the situation where a missing brace in the middle of a script cannot be detected until the end of the script, and it can take quite a bit of effort to go through the whole script trying to match up each opening brace with its corresponding closing brace. I have heard that some programmers try to avoid this problem by automatically appending some extra closing braces at the end of each script, but this only avoids the generation of an error message without preventing the code from continuing down the wrong path.

Such a problem did not exist in an earlier language which I used as it did not utilise opening and closing braces but instead provided the ability to end each structure with an end<structure> statement. Thus if you had nested structures of different types and accidentally missed out the corresponding end<structure> statement, or even inserted the wrong end<structure> statement, the compiler was instantly able to report the actual line number where the error was detected.

Although PHP does provide for alternative syntax for control structures I have found that this alternative is rarely used, especially in all the code samples I have seen, so I am following the crowd by using braces. However, I do employ the following rules:

You may not like my rules, but ask me if I care.

Calls to is_True()

A clueless newbie called EnragedMikey posted this comment:

I like the is_True() calls.

You may not be aware of the need for such a function, but when you grow up and start doing some big boy programming which involves communicating with various database engines you will become aware of one simple fact - not all databases support the BOOLEAN data type. In this case there are various options:

There may also be some settings in config/ini files which use the following values:

Because of all these possibilities I created a simple is_True() function as follows:

function is_True ($value)
// test if a value is TRUE or FALSE
{
    if (is_bool($value)) return $value;

    // a string field may contain several possible values
    if (preg_match('/^(Y|YES|T|TRUE|ON|1)$/i', $value)) {
        return true;
    } // if

    return false;

} // is_True

This allows me to deal with all possible combinations with a simple function call, so it's not as stupid as it looks.

This is PHP 4 code

A clueless newbie called 0x18 posted this comment:

Interface? This is PHP 4 code.

It may come as a great surprise to you, but a great deal of the functionality which existed in PHP 4 still survives to this day in PHP 7 simply because it is useful. While there have been numerous additions and changes with PHP 5 and PHP 7 I have ignored most of them for one simple reason - I cannot find a use for them. Some of the additions provide functionality which I do not need, while others simply provide a different way of doing something that can be done already. I am an old school engineer who follows the maxim "if it ain't broke don't fix it", which means that if I already have code which works then why should I spend time and effort in changing it so that it produces the exact same result, but differently? I have to weigh up the cost of making the change with its benefits, and if there are no visible benefits then any effort would be a waste of time.

A prime example of this concerns arrays. In all versions of PHP an array can be constructed with code like this:

$array = array(1,2,3,4,5);

PHP 5.4 introduced the short array syntax which allows the same result to be achieved with slightly fewer keystrokes:

$array = [1,2,3,4,5];

While some dimwits out there may think that this is a good idea, I do not, and I do not see the point in trawling through my entire code base, which has been continuously growing since 2003, to make a change that has zero benefit. Some of you may think that employing the latest addition to the language means that you are "with it" and "fashionable", and if you don't you are "out of date" and writing "legacy code", but I do not. I see my job as the ability to write code which solves problems for my paying customers, and once I have solved a problem I file the code away and move on to the next problem. My paying customers are only interested in code which is functional, not fashionable, and they are only willing to pay me for dealing with a change in functionality, not fashion.

This approach also avoids the problem highlighted in When is Enough, Enough?.

I am not the only one who thinks that using the latest features in the language simply because they are shiny and new may not actually be a good idea. This is known as the Magpie Syndrome and is discussed by Matt Williams in his article Oooh Shiny! Magpies don't know what's just enough! as well as Does Your Team Have STDs?

This class is utterly terrible

A clueless newbie called codays posted this comment:

Yes, the class is utterly terrible, ....

There then follows a list of complaints which I shall address one at a time.

it violates every principle of software engineering

If you mean the The basic principles of OOP then you should understand that I have managed to run through all the crap written about OOP and have managed to filter out what OOP is as well as what OOP is not.

If you mean the SOLID principles then you should read why I think that they are not so solid after all. I cannot follow those principles which do not apply, and I will not follow those principles with which I do not agree.

As for other principles such as seeking high cohesion and loose coupling, if you bothered to read my code properly and compared it with the crap that you obviously write, you should see that I score high marks in both categories.

According to the Gang of Four my use of an abstract class which is inherited by concrete classes is the best way to avoid problems with inheritance, and as multiple inheritance is not supported it should be obvious that the same result cannot be achieved with multiple abstract classes. It should also be obvious that instances of the Template Method Pattern, which is used quite heavily in the RADICORE framework, must be defined in a single abstract class.

Writing code is more about achieving results as quickly and efficiently as possible and less about following a series of artificial rules. It is more about pragmatism and less about dogmatism.

is deeply unmaintainable

Really? I have had absolutely no problem in maintaining and enhancing this code since its inception in 2003, and neither has my business partner and his team of programmers.

exhibits a weak grasp on boolean logic and how basic control flow constructs work

You are talking out of the wrong end of your alimentary canal.

disagrees with any sensible style guide that has ever or will ever exist

Rubbish. There is absolutely no such thing as a single style guide which every programmer on the plant is obliged to follow. I have been programming for over 30 years, and I have been on many different teams for many different companies, and each of them had their own style guide. Every one of these style guides was different, sometimes contradictory, so it was impossible to follow all of them all of the time. Some of my experiences have been documented in Development Standards - Limitation or Inspiration? and On not using the "right" standards.

trashes the SRP so hard that it's almost painful to look at

Then you clearly do not understand the true meaning of SRP.

and reads a bit like the result of just running the last twenty Daily WTF posts through a shredder and then having a blind man glue the results back together randomly

By saying that you are showing that you have done nothing more than noticed that my code does not follow the same rules as yours, and it does not adhere to the same coding style as yours. But why should it? There is no such thing as a single set of rules or a single programming style which every programmer is obliged to follow as different groups of programmers adopt whatever rules or styles which suit them best. What one group defines as excellent another group could define as excrement. This idea is expressed in the phrase One man's meat is another man's poison.

The idea that you should not have more lines of code in a method than can fit into a single screen on your monitor because your brain cannot cope with more than one screenfull of data at a time is quite ludicrous. A typical object may require hundreds lines of code to deal with all of its business rules, so when you are maintaining or debugging it you need to be aware of all those lines. If you don't have the brain capacity to deal with that number of lines in a single class then you sure as hell would struggle even more if those lines were split across multiple classes.


A message for Robert C. Martin

A clueless newbie called Martel_the_Hammer posted this comment:

Robert Martin would shit bricks if he saw this...

To which that clueless newbie called Hall_of_Famer replied:

Uncle Bob's reaction:

https://cdn.discourse.org/sitepoint/uploads/default/23597/79c18c0a5d43158a.jpg (broken link)

That link pointed to an image which did not contain an intelligible response, so my message to Robert C. Martin is this:

You really need to brush up on your communication skills. Your original definition of SRP, that of "reason for change", was so woefully inadequate and imprecise that it was open to far too much interpretation. Some clueless newbies took the idea of splitting so far that they would take a perfectly cohesive module and split it into tiny fragments, thus decreasing cohesion and increasing coupling, both of which are deemed to be bad things. Some even think that SRP is completely different from Separation of Concerns (SoC) for such ridiculous reasons as:
You eventually provided a more meaningful definition by identifying three areas of logic - UI, business rules and database - which incidentally creates the same levels of separation as the 3-Tier Architecture on which my framework was based from the outset, but even this can be perverted by some. For example:
If you were any good at your job you would be able to provide a more meaningful description, with examples, of what this principle really means so that it would be less confusing for all those clueless newbies out there.

The problem with the idea that you should take a large piece of code which does "too much" and break it down into smaller cohesive modules each of which does "just enough" is that if you go too far you will end up with a plethora of tiny modules each of which does "too little". So how do you recognise "too much", "too little" and "just enough"? This question was raised by Brandon Savage in his article Avoiding object oriented overkill in which he said:

The concept that large tasks are broken into smaller, more manageable objects is fundamental. Breaking large tasks into smaller objects works, because it improves maintainability and modularity. It encourages reuse. When done correctly, it reduces code duplication.
...
But once it's learned, do we ever learn when and where to stop abstracting? Too often, I come across code that is so abstracted it's nearly impossible to follow or understand.

For me the definitions of "too much" and "too little" are quite straightforward:

It's not the Rules which count, it's the Results

Whenever I am told "You are breaking the rules!" my immediate response is to ask "What rules?" There is no single published set of rules which all programmers are obliged to follow, only a mish-mash of genuine rules, guidelines and personal preferences. Quite often a "rule" by one person can be totally contradicted by another, so whose version is correct? This topic is discussed further in You are not following Best Practices.

The big problem is that these "rules" never originated from a single authoritative source, they have been added to and re-interpreted over several decades by any Tom, Dick and Harry with an opinion. This is like trying to combine the recipes from a multitude of different cook books and expecting the result to be a gourmet meal when in fact it is more likely to be a dog's dinner.

Those who think that by following the rules the results which are obtained are automatically acceptable are nothing more than dogmatists. Those whose primary concern is the result, and who ignore any artificial rules which impede that result are called pragmatists. This topic is discussed further in The difference between "dogmatic" and "pragmatic".

What you clueless newbies totally fail to take into consideration is the results of my approach, and these results can only be achieved by following those rules which can be proved to be beneficial and ignoring all the others. This is mentioned in the results of my approach.

As has already been stated in What OOP is the basic definition is 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.

The idea is that the more reusable code you have at your disposal then:

I have used my heretical methods to build a large enterprise application which consists of 400+ database tables and 3,500+ user transactions, and the amount of reusable code beats your paltry offerings into a cocked hat:

The only unique components which have to be produced are as follows:

After having created a new table in my database I can use my heretical methods to do the following:

I can do all this in 5 minutes without the need to write any code - no PHP code, no HTML code and no SQL code. If you cannot achieve the same level of productivity with YOUR development environment I strongly suggest that you stop telling me that my obviously superior methods are wrong and start learning how the experienced pragmatists can write better code than you clueless newbie dogmatists.

Here endeth the lesson. Don't applaud, just throw money.

Comments from the not-so-clueless

Believe it or not there was a comment from someone who could not see anything wrong with my approach.

A wise person called KiwiThunda made this comment:

This has the single responsibility of being a working back-end. I see nothing wrong here.

References

Here are some articles I have written on my framework:

Here are some heretical articles I have written on the topic of OOP:


Other comments are available on this reddit post where, as usual, adult debate has given way to childish insults.

counter