After spending 20 years building enterprise applications with languages such as COBOL and UNIFACE, in 2002 I decided to switch to a new language so that I could build the same type of application for the internet. I chose PHP as it was purpose built for creating dynamic web applications which pulled their content from a relational database. It also introduced me to the black art of Object Oriented Programming (OOP). I did not attend any training courses on OOP as I was already an accomplished programmer who had designed and built both a library of common routines and then a framework in two different languages which had increased the productivity of the entire development team. I felt that I could easily train myself by reading the manual and looking through sample code which I found in books and various online resources.
I should point out that while I had been following the principles of KISS, DRY and YAGNI I had no knowledge of such things as design patterns or best practices such as SOLID and GRASP as they were unique to OO languages. That turned out to be a blessing in disguise as I later realised that they were so badly written and open to so much interpretation, and therefore misinterpretation, that the results which they achieved were highly questionable. I discovered that I could achieve better results, i.e. more reusable code, by ignoring them completely and following my own nose. I particularly disagreed with the idea that OOP required a completely different approach to the way that systems needed to be designed simply because I had already learned that the most important part of any database application was the design of the database itself, and once that had been done then the code should be structured around that design. I did not change my design methodology when I switched from COBOL to UNIFACE, and I saw no reason to change it when I switched to PHP. While the syntax of each of those languages may have been different, the basic programming principles were the same.
I started by building a small proof of concept (POC) in the form of a small sample application which I published in November 2003. This proved to me how straightforward it was to write reusable code which could read database data and convert it into HTML using the XSL templating engine. This was so successful that I went ahead and produced a third version of my framework to provide standard functionality such as a login screen, dynamic menus and Role Based Access Control (RBAC) which were common to all the database applications which I had developed previously. I released this framework as open source in 2006 under the name RADICORE.
Note that there are significant differences between my approach and the one taught by all the OO "experts":
Since a major motivation for object-oriented programming is software reuse it should follow that the more code you can reuse then the more successful you are at OOP. The more code you can share then the less code you have to write and the quicker you can get the job done. It is all about striking a balance between the code that you *DON'T* have to write and the code that you *DO* have to write. If the ultimate goal is to spend the minimum amount of time in writing boilerplate code so that you can spend the maximum amount of time on the business logic, then the fact that the standard modules within the RADICORE framework, as shown below, provide 100% of the boilerplate code should be significant.
Below is a small diagram of the structure used in the RADICORE framework. It started by being an implementation of the 3-Tier Architecture, but later evolved to be combined with the Model-View-Controller (MVC) design pattern, as shown below in Figure 1:
Figure 1 - MVC plus 3 Tier Architecture
Note that there is a more detailed diagram in RADICORE - A Development Infrastructure for PHP.
Each of the components in the above diagram is a hyperlink which will take you to a detailed explanation.
Following the Evans Classification these components have the following types:
You should notice that the framework does not require any component to be hand-crafted by the developer. All Controllers, Views and DAOs are built into the framework, and all Models are generated by the framework and already contain common code which is inherited from an abstract class. This includes all standard data validation, with the developer only needing to insert the code for any non-standard business rules into the various "hook" methods which have been provided.
In answer to the question How effective is the RADICORE framework?
I point to the fact that in 2007 I started to build my first ERP package, which was called TRANSIX, by picking six database designs from Len Silverston's Data Model Resource Book, then generating the class files and user transactions from the database schemas. I then added in the business rules to the "hook" methods and after six months I had a prototype which I could demonstrate to a client. That is six months for six databases, which is an average of one month per database. If you think you can match that then prove it by taking this challenge.
When my critics started telling me You're not injecting your dependencies
I had to do a google search to find out what is was and how to do it. I looked at the code samples and I could see straight away that there was nothing like that in my code. As my code already worked I could see no benefit in changing it to match those samples, so I decided to ignore them. The only thing it prompted me to do was to write Dependency Injection is EVIL. While writing this I suddenly realised that I actually do use dependency injection but in a totally novel way. It was so novel that none of my critics spotted it, which just shows you how bright they were. It became quite obvious to me that few programmers had ever gone to the source of this principle by reading The Dependency Inversion Principle which was written by Robert C. Martin (Uncle Bob) in 1996, and instead were regurgitating some second hand interpretations of how it should be done without first identifying why it should be done.
As you will read below I found the document which written by the author of this principle, read what he wrote, and then worked out how to implement it to produce the maximum benefit with the minimum of effort. Instead of following the herd and duplicating other people's implementations I created my own version, but because it was new and different they failed to spot that it achieved the objectives of dependency injection without copying any existing implementation, and so I was accused of being a maverick and a heretic.
In the RADICORE framework I use Dependency Injection in the following places:
I do NOT use Dependency Injection to perform any of the following:
In my investigation into the ways in which dependencies can be handled I came across various statements which show a lack of comprehension on the part of the author of those statements, such as the following:
1. Dependency injection aims to separate the concerns of constructing objects and using them
Rubbish. There are several things wrong with that statement:
some implementationsas my implementation employs a different (and therefore heretical) technique.
the degree to which the responsibilities of a single module/component form a meaningful unit, so when you consider that constructing an object and calling one of its methods can take a minimum of two lines of code, I fail to recognise how two lines of code, as shown below, can be considered to be separate responsibilities.
$instance = new classname; $result = $instance->method($arg);
The Single Responsibility Principle was first documented by Robert C. Martin, and while he talks about separating concerns such as presentation logic, business logic and database logic, each of those areas of logic requires many lines of code, and only someone suffering from a severe attack of pedantry and dogmatism coupled with a lack of common sense would treat those two lines of code as being unrelated and therefore candidates for separation. As such I treat this wild interpretation of SoC as being nonsensical and therefore without merit.
2. DI leads to loosely coupled programs.
You have this backwards. DI does not lead to loosely coupled programs, it follows as a result of having loosely coupled programs. As described below in Robert C. Martin's example of a COPY program, DI provides the ability to select the dependent object at run time. This means that you must have a choice of several objects which share the same method signature. This is a result of polymorphism which allows different objects to be plug-compatible. This situation can only exist if your modules have loose coupling. Modules which have tight coupling are not interchangeable and therefore cannot be swapped at run time. To summarise:
3. The pattern ensures that an object or function that wants to use a given service should not have to know how to construct those services.
Why should it be necessary to use anything other than the new operator in order to instantiate a class into an object? Who on earth writes classes that require external code to identify how they should be configured? In my framework each concrete table class has only one configuration, and that is handled within the class constructor. What is the purpose of a constructor if not to construct the instance? The clue is is the name, you numpty. Any code which is necessary to configure an abstract class into a concrete class is contained within the class constructor of the subclass, so all knowledge of how to construct the object is contained within the class itself, not in any external code. This follows the principle of implementation hiding which is a fundamental part of encapsulation. If I don't need external code to construct an object then what is the benefit of following this pattern? Surely that would be a violation of YAGNI?
4. The receiving "client" (object or function) is provided with its dependencies by external code (an "injector").
There is no rule which states that EVERY dependency MUST be injected by external code. While I agree that there are benefits in doing so when a dependency can be supplied from a number of plug-compatible objects, where there is never a choice between several alternatives then surely the act of adding in code to switch to an alternative which does not exist would be a violation of YAGNI?
There is no rule which says that the dependent object must be instantiated before it is injected. This is just a limitation built in to the early compiled and statically typed languages. With PHP, which is neither, it is possible to inject nothing more than the identity of the dependent class into the receiving client using a string variable which can then be instantiated into an object within that client. Same effect, but different code.
In order to find out what this thing called "dependency injection" was and if it was worth my while to implement it I searched the interweb thingy for a proper definition and some examples of how it could/should be implemented. This is when I came across the The Dependency Inversion Principle which was written by Robert C. Martin (Uncle Bob) in 1996. I read the words and studied the example given in the "Copy" program and made the following observations:
read() and write() methods. Multiple objects sharing the same method signatures is called polymorphism.It was quite obvious to me that DI was invented for no other reason than to take advantage of polymorphism, where multiple objects share the same method signatures. This allows an object which calls one of those signatures to be used with any of those objects. This is also the only mechanism I know of which does not use code within the client to choose which one of those dependent objects should be used by the client. This is the reason why I do not use any form of dependency injection if there is only a single object that can be used as a dependency.
I also noticed that, according to the Evans Classification, the COPY program is a service while each of the device objects is an entity. Uncle Bob's article did not cover the possibilities of injecting a service into an entity, or an entity into an entity, so I surmised that those combinations were illogical. This thought was reinforced when I came across the following:
As the COPY program was not a useful example when it came to building web pages in PHP I searched the interweb thingy again for examples from all those OO "experts" who supposedly exist in cyberspace. I was not impressed with what I found. None of the sample code I came across looked as if it could be fitted into my framework without major surgery and without violating the KISS principle. All I could see was my simple code which worked being replaced with complex code which achieved the same result but in a more roundabout and convoluted way, and I was not impressed. As I investigated further I became even more unimpressed as I realised that instead of starting with the purpose of DI, which is to take advantage of polymorphism, they were all concentrating on how it could (or should) be implemented. They tried to deduce the purpose of DI by reverse-engineering its implementation, and it was obvious to me that their conclusion was wrong. Because of this I decided not to follow their "advice" as it was obviously based on erroneous assumptions.
What I found amazing is that in all that I read about dependency injection, even in Uncle Bob's original article, it was never identified that its purpose was to provide a mechanism to take advantage of polymorphism. What is the point of polymorphism if you cannot use it? How may ways are there of using polymorphism? While everybody else is absorbed, perhaps even obsessed, by how it can be implemented, nobody has made this simple connection. They cannot tell that my implementation is effective, just different, so all they can do is complain about the difference.
While Dependency Injection is clearly demonstrated in the example COPY program which was provided in Robert C. Martin's original article called the The Dependency Inversion Principle I later came across this wikipedia article which had the same title but talked about something completely different. On top of this they took Robert C. Martin's original idea and re-named it to Dependency Injection. We now end up with the following completely different and unrelated principles:
Dependency Injection is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally. Dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs. The pattern ensures that an object or function that wants to use a given service should not have to know how to construct those services. Instead, the receiving "client" (object or function) is provided with its dependencies by external code (an "injector"), of which it is not aware.
This matches the example COPY program. There are several ways in which a client can receive injected services:
While the RADICORE framework does contain code which injects dependencies (Models) into a client (Controller or View), it has a particular implementation which I have not seen documented anywhere else. That implementation is the subject of this article.
Inversion of Control is a design principle in which custom-written portions of a computer program receives the flow of control from an external source (e.g. a framework). In procedural programming, a program's custom code calls reusable libraries to take care of generic tasks, but with inversion of control, it is the external code or framework that is in control and calls the custom code.
This is also known as the Hollywood Principle (don't call us, we'll call you) which is implemented using the Template Method Pattern. This uses an abstract superclass which contains a mixture of invariant/fixed methods which define the skeleton of an operation in terms of a number of steps. These fixed methods are interspersed with a number of variable/customisable "hook" methods which can be overridden in any concrete subclass in order to provide custom processing within that subclass. The developer does not have to write any code to call a customisable method in a subclass as that call is already provided in the superclass.
In the RADICORE framework there is an abstract table class which is inherited by every concrete table class. This abstract class provides all the boilerplate code, which includes primary validation, to handle the communication between the front-end Presentation layer and the back-end Data Access layer. Each concrete subclass may contain their own implementations for any of the customisable "hook" methods in order to process the business rules which are specific to that subclass. Every method called by a Controller on a Model, or a View on a Model, is a template method.
Dependency Inversion Principle is a specific methodology for loosely coupled software modules. When following this principle, the conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are reversed, thus rendering high-level modules independent of the low-level module implementation details.
The principle states:
- High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).
- Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
Try as I might I simply do not understand what is being said in this description. It uses a bunch of clever-sounding words which give the appearance of knowledge but which totally fail to point out that DI depends on having modules which are polymorphic and therefore plug-compatible. It says that the conventional dependency relationship is reversed
, but I cannot see how this is achieved. If ModuleA calls ModuleB then ModuleB is the dependency. If ModuleB calls ModuleA then ModuleA is the dependency. The module which is being called is the dependency, and this relationship between the caller and the callee cannot be reversed. It appears to say that if a client is dependent on another object then it is not supposed to call a method directly on that object, instead it calls an intermediate object interface (which does not contain an implementation) and somehow the interface transfers the call to an object which does contain an implementation. This does not compute. It is simply not possible to call a method without specifying an object which contains that method. I created my framework in PHP 4 which did not support interfaces, so I know for a fact that I do not need to go through an interface before I call a method.
To me that description is utterly meaningless unless it is supported by sample code which proves that its claims have merit. As a pragmatist I cannot follow a principle unless I have good reason to do so, and that description does not provide a good enough reason.
Perhaps this implementation was chosen for the simple reason that in the early compiled and statically typed languages it was only possible to use the new operator with a hard-coded class name. This limitation does not exist in PHP which is interpreted and dynamically typed. It is possible to provide the class name in a string variable instead of forcing it to be hard-coded. Note that the PHP manual states that As of PHP 8.0.0, using new with arbitrary expressions is supported
but this is crap as I was using this technique way back in 2002 with PHP 4.
The description of this principle talks about Policy layers, Mechanism layers and Utility layers which do not exist in either the 3-Tier Architecture or the Model-View-Controller design pattern which form the backbone of my framework (as shown in Figure 1 above). Its implementation of the Model-View-Controller looks nothing like mine, and it refers to a host of other related patterns which do not appear in my framework. It is supposed to be based on Robert C. Martin's original idea, but to me it looks like someone has thrown a bucket of clever-sounding but unrelated words into the mix and created something which is as useful as a chocolate teapot.
I find that putting the two words "dependency" and "inversion" in the same sentence to be completely confusing as they are completely separate principles. Here is an example of a misleading statement which I found in A quick intro to Dependency Injection: what it is, and when to use it from freecodecamp.org:
Inversion of control - the concept behind DI
This states that a class should not configure its dependencies statically but should be configured by some other class from outside.
This makes no mention of polymorphism nor the swapping of dependent objects. Inversion of Control is having a framework that calls the code you write instead of you having to write code to call functions in a library while Dependency Injection is about swapping objects at runtime. One of these relies on multiple subclasses inheriting from the same abstract class to provide polymorphism, while the other relies on the abstract class to implement the Hollywood Principle via the Template Method Pattern. All this is achieved without any object interfaces.
Inversion of Control and Dependency Injection are unrelated principles. Mixing the two up causes nothing but confusion, so anything written by people who add to this confusion is not worthy of consideration and can be ignored.
In my long career I have worked with many people with varying degrees of experience and ability. In that time I have made the following observations:
There are two ways in which an idea can be implemented - indiscriminately or intelligently.
The indiscriminate use of an idea shows that the user does not have the ability to understand what the phrase when appropriate
means. They do not understand that implementing an idea in the wrong way or in the wrong place, such as inserting code to deal with a choice of dependent objects when there is no choice, is not only a waste of time (it actually violates the YAGNI principle), they are actually making the software less maintainable by causing a future maintainer to read code that is totally useless. This causes confusion instead of clarity.
Some people know only what they have been taught while others know what they have learned.
Those who follow what they have been taught without thinking on the assumption that their teacher is an expert and should never be contradicted or even questioned often do not realise that those teachings may be out of date, may have been based on wrong assumptions, or may have been compiled by users of compiled and statically typed languages which are no longer relevant in the modern world of interpreted and dynamically typed languages. How many of those programming principles which were created for the Smalltalk language in the 1970s are actually still valid in the 2020s, some 50 years later.
If programmers do not understand the purpose behind an idea, the problem it was designed to solve, then simply duplicating someone else's implementation may not yield the best results. This runs contrary to the notion of constructionist learning in which students start with a set of ideas and refine them through experimentation in which they build solutions to real-world problems. I created my prototype by taking what was taught by others and experimenting with it until I came up with something simpler and better, something which produced greater volumes of reusable code. Those who simply duplicate what others have done without understanding why it was done in in that particular way are in great danger of becoming nothing more than Cargo Cult Programmers. The problem with such people is that because they have been taught by and are surrounded by other Cargo Cult practitioners they cannot tolerate anyone who strays from the path and who dares to think for themselves.
An example of this can be found in a sitepoint thread in 2015 title Dependency Injection: a discussion of the pros and cons which discussed an article which I wrote in 2011 called Dependency Injection is Evil. One particular critic complained that I was not injecting dependencies into any of my Model classes even though I showed that it was not appropriate to do so. Some of the statements made in that thread caused to to update that article with Criticisms by so-called "experts". Some of those statements are repeated below:
1. There are no circumstances in which DI is inappropriate.
This was in response to my statement that I never use DI to inject a Model into another Model as I do not have multiple choices for that dependent Model. For example, when I am in the Person object and I wish to obtain the postal address then I access the PostalAddress object, of which there is only one. This means that inserting code to deal with multiple choices when there are no multiple choices would not be appropriate. In fact it would be a violation of YAGNI. His statement clearly demonstrated that he had failed to understand the logic behind the example of the COPY program provided in Uncle Bob's article which clearly shows the following:
read() and write() methods. When multiple objects support the same methods this is called polymorphism.This opinion is shred by others, such as When to inject: the distinction between newables and injectables where it says:
Services depends on entities, while the opposite should not happen.
The same point is echoed in How to write testable code where it says:
A newable [entity] is a class that can just be instantiated (e.g. with the 'new' operator) as needed in code.
An injectable [service] should be injected (typically via constructor injection) in its client.
Do yourself a favor and don't inject Services [injectables] into newables [entities].
This is precisely why I only inject entities into services. I have hundreds of alternative entities (Models) in my application, and each of my Controllers and Views can function with any one of those entities.
2. You are claiming that an alternative to DI is to use a singleton
I claimed no such thing. What I actually said was that an alternative to using DI was NOT to use DI, which can be achieved with or without the use of a singleton. If the stated purpose of DI is to separate the construction of an object from its use then my alternative is NOT to perform that separation at all. Note also that I do not use the factory method pattern as every one of my Models has a single configuration which is loaded in the class constructor. The idea of putting that code in another method and then having additional code to call that method strikes me as being a complete waste of key strokes and a violation of YAGNI. The only time I require an external configuration is when I define that configuration in my version of a Container before I activate a Controller.
When I do not wish to use DI to split the instantiation of an object from a call on one of its methods I execute those two steps, which by the way require no more than a single line of code each, using code as simple as the following:
$object = singleton::getInstance('foobar'); $result = $object->method($arg);
Here is another version which is much longer:
require 'classes/foobar.class.inc'; $dbobject = new foobar(); $result = $object->method($arg);
I do not see that these few lines of code constitute separate responsibilities or concerns as described by Robert C. Martin, Martin Fowler or Craig Larman, therefore I see no justification for separating them.
3. You are arguing that DI doesn't have any benefits.
This "expert" is mis-quoting me again. DI has benefits when it is used in appropriate circumstances (i.e. when there is a choice of multiple objects which can be used as a dependency), but it has zero benefits, and also violates YAGNI, when it is used in inappropriate circumstances (i.e. when there is only a single object which can be used as a dependency). It is plain to me that this numpty does not have the brain capacity to understand what "when appropriate" means.
When I started teaching myself PHP I noticed straight away that procedural and object oriented programming are the same in that they are both designed around the idea of writing imperative statements which are executed in a linear fashion. The only difference is the way that the code can be packaged. In procedural programming a function can only be defined once, and after being called it disappears from memory. In object oriented programming several functions can be grouped together in a class, and the same function name (now called a method) can be duplicated in any number of classes. A class must be instantiated into an object before any of its methods can be called. After a method has been called the object still resides in memory, and this allows the data within the object to be viewed or modified by calls to other methods. Methods can be called on an object for as long as it exists within memory, which is until it is destroyed.
By reading the PHP manual I learned the mechanics of encapsulation and inheritance. I also consulted a few online resources and bought a few books to see sample programs written by others. Sadly none of these provided examples of polymorphism or how it could be used, so I left that for later. I had read that the main motivation for using OOP was to increase code reuse and decrease maintenance, so that is what I strived to achieve. After I had built several objects which shared the same method names I realised that those methods could be defined once in an abstract class and shared through inheritance. I also saw that a piece of code which called those methods could be made to function with any object, so I tried several experiments until I found a simple mechanism to achieve maximum reusability. That simple method is the subject of this article.
When I started to write my own PHP code I began with a Sample Application as a proof of concept. There were two design decisions I made from the outset based on what I had learned with the writing of database applications in other languages in the previous 20 years:
My first step was to create a Model class for a database table. As I already knew that every table needed to support the same CRUD operations I create a separate method for each. I did not follow the practice I saw in some of other people's code samples of having separate functions for load(), validate() and store() as I had already learned to put such groups into wrapper functions such as insert(), read(), update() and delete(). I started by copying some code which I found in a book so that I could experiment with it.
<?php require 'screens/person.detail.screen.inc'; // This identifies the XSL stylesheet require 'classes/person.class.inc'; // This identifies the Model class $dbobject = new Person(); $dbobject->setUserID ( $_POST['userID'] ); $dbobject->setEmail ( $_POST['email'] ); $dbobject->setFirstname ( $_POST['firstname']); $dbobject->setLastname ( $_POST['lastname'] ); $dbobject->setAddress1 ( $_POST['address1'] ); $dbobject->setAddress2 ( $_POST['address2'] ); $dbobject->setCity ( $_POST['city'] ); $dbobject->setProvince ( $_POST['province'] ); $dbobject->setCountry ( $_POST['country'] ); if ($dbobject->insertPerson($db) !== true) { // do error handling } ?>
An alternative to this would be to pass each column as a separate argument on the method call like in the following:
$result = $dbobject->insertPerson($_POST['userID'], $_POST['email'], $_POST['firstname'], $_POST['lastname'], $_POST['address1'], $_POST['address2'], $_POST['city'], $_POST['province'], $_POST['country'], );
I did not like any of this code for the following reasons:
These have the effect of making each Controller tightly coupled to a particular Model, and as every competent programmer knows (or should know) tight coupling is the enemy of polymorphism and therefore the enemy of reusability. After a little experimentation I changed it from being tightly coupled to loosely coupled as shown below in My version of a Client.
Here is my version of a client module (in this example it is a Controller), one that has dependencies.
<?php // page controller script for the ADD1 pattern require 'classes/$table_id.class.inc'; require 'screens/$screen.screen.inc'; $dbobject = new $table_id; $result = $dbobject->insertRecord($_POST); if ($dbobject->errors) { // do error handling } ?>
The simple nature of this code relies on the fact that every Model subclass inherits the same set of methods from the abstract superclass, therefore the methods which are called can operate on any Model class. It performs the following simple steps:
$fieldarray as both an input and an output argument. This means that none of these methods is tied to a particular set of column names.Each Controller can operate on any table class and can have no more than one View. There is a central library of 45 reusable Controllers, one for each of my Transaction Patterns.
The following code samples were copied from DI, DiC, & Service Locator Redux by Ralph Schindler:
A Service Locator, on the other hand, is better defined by a usage pattern, rather than the signature (pattern) of any particular class. This means that any object, upon being injected into a consuming object and then asked for an object, can be a service locator if it is capable of producing the requested object by a particular name or type. Effectively, a service locator can be a special container, or not; it can be a registry, or not; or it can be a factory, but perhaps not.
The only stipulation is that you've provided an object (the service locator) as a dependency to another object so that that the consuming object can then use the provided object (the service locator) to locate any dependencies. Instead of injecting n dependencies, you inject just one: the service locator. In code, it looks like this:
$container = new ContainerThatCanFindThings(); // implements LocatorInterface $thing = new ThingThatHasDependencies($container);
The constructor for this ThingThatHasDependencies, might look like this
class ThingThatHasDependencies { public function __construct(LocatorInterface $container) { $this->dependencyOne = $container->get('DependencyOne'); $this->dependencyTwo = $container->get('DependencyTwo'); } }
Now, you can see that any instance of ThingThatHasDependencies is now Container aware, which, depending on the context of the ThingThatHasDependencies, might be a good (or a bad) thing.
In the RADICORE framework the ThingThatHasDependencies is equivalent to a Controller or a View which are both dependent on one or more Models. Note that the $container has to be built before it can be injected into the CONTROLLER. The structure of the necessary components to support the orthodox implementation is shown below in Figure 2:
Figure 2 - Diagram of an orthodox DI Container
This functions as follows:
Here is my version of a DI Container.
<?php // component script $table_id = "person"; // identify the Model $screen = 'person.detail'; // identify the View require 'std.add1.inc'; // activate the Controller ?>
Note the following:
<table>.class.inc and exists in the subsystem's classes subdirectory.screens subdirectory.INCLUDES directory which is identified using the include_path directive.Instead of having two pieces of code to first build the CONTAINER before instantiating the CONTROLLER so that the CONTAINER can be injected into it I launch the CONTAINER (which is documented as a component script) directly from the URL. This means that my CONTAINER identifies all the dependencies as well as activating the component that will use those dependencies. This achieves the same result as the orthodox method as shown in Figure 2, but with fewer and simpler components. Note that the component identified as SCRIPT #1 is no longer required.
Figure 3 - Diagram of my heretical DI Container
This functions as follows:
Note the following:
When other programmers look at my implementation of best practices the only thing that they seem to notice is that they are unfamiliar to them, that they are different. Because they do not understand how my code can be so different and still be "correct" they instantly assume that it must be "incorrect", that I am not following "best practices". They fail to realise that what I am actually doing is following the same set of practices, but only when appropriate and using a different interpretation and a different implementation, one that, in my opinion, produces the best results. The main motivation for object oriented programming is to create more reusable code as this leads to less maintenance, so the best results can be measured by the volume of reusable code which manifests itself by the elimination of as much boilerplate code as possible, thus enabling the developer to spend more time on the important code, the business rules. If you read Write Only Business Logic: Eliminate Boilerplate you will see that, using my heretical interpretation of "best practices" I have eliminated 100% (yes, ONE HUNDRED PERCENT) of the boilerplate code. As far as I am aware there is no other framework that can achieve this level of reusability, so there is no other framework that can claim to be better.
As an example I shall refer to an ERP Application which was written entirely using the RADICORE framework:
If I had followed the orthodox implementation of "best practices" it would be comprised of the following:
What few programmers fail to spot in the above description are the places where the Single Responsibility Principle (SRP) is violated:
The following components would have to be built by hand, which would take time to both design and then build:
Due to the absence of common method names and the abundance of unique property names, each requiring its own setter and getter, each Controller or View would be tightly coupled to a single Model, thus eliminating any polymorphism and the possibility of sharing components via dependency injection.
My unorthodox and heretical approach promotes loose coupling and maximises the opportunities for polymorphism and therefore reuse by dependency injection. This is because of the following:
While these transactions are fully functional in that they automatically perform primary validation, additional business logic can be added in afterwards by inserting the relevant code into any of the predefined "hook" methods
This means then that if I have 45 Controllers which can be reused with any of my 400 Model classes I therefore have 45 x 400 = 18,000 (YES, EIGHTEEN THOUSAND) opportunities for polymorphism and dependency injection.
You should notice that my implementation adheres to the Single Responsibility Principle (SRP) in the following ways:
If the purpose of object oriented programming is to increase reusability and thereby decrease maintenance then how much reusability can be achieved and how can it be measured? The answer is given in Write Only Business Logic: Eliminate Boilerplate where it states that the only code which has value to the paying customer is that which processes the business logic. Everything else, which has been called glue code or, more commonly, boilerplate code, should be regarded as a candidate for being replaced with reusable modules instead of being duplicated in multiple places. When you consider the fact that in the RADICORE framework every user transaction can be generated by pressing buttons on a screen without the need to write any code - no PHP, no HTML, no SQL - this means that 100% (yes, ONE HUNDRED PERCENT) of the boilerplate code is provided for the developer and does not have to be written by the developer. Any additional business logic which is required can be added into individual concrete table subclasses using the various "hook" methods which have been built into the abstract table class.
So, the answer is that it *IS* possible for a framework to provide ONE HUNDRED PERCENT of the boilerplate code. If your framework cannot match that then I would suggest that you need to look for a framework that was written by someone more competent, someone who understands the reasoning behind all these programming principles instead of just copying the faulty implementations made by others..
Here endeth the lesson. Don't applaud, just throw money.
| 04 May 2026 | Added Overview of the RADICORE framework
Added What Dependency Injection is NOT Renamed What is Dependency Injection (DI)? to What Dependency Injection IS |