I’ve come up with several questions while reading this discussion that I hope getting answers to can help clarify a few things for me:
@tony_marston404
Is your example of Dependency Injection actually an example of that principle:
I ask because I believe it is not. Instead, I think yours is an excellent example of one way to exercise Inversion of Control (http://en.wikipedia.org/wiki/Inversion_of_control), a paradigm that Dependency Injection was designed to satisfy, but does not illustrate DI itself.
This code sample illustrates how the idea of inverting control can be beneficial. You’ve been able to leverage this idea to implement 2,400 component scripts using only 40 controllers. As it is used in your framework you obviously appreciate the value provided by this.
The code sample does not, though, demonstrate Dependency Injection. This code allows you to instantiate a single instance of the designated class with exactly 0 constructor arguments. This code, provided as an example of Dependency Injection within your framework, does not illustrate that pattern.
This may be just a small terminology difference, but it seems that cyclic nature of this discussion might be the result of something that silly.
If this is not the case, I sincerely hope I haven’t offended you. My only hope here is to help push this discussion forward and help someone (hopefully myself) learn something.
@TomB I’d be especially curious to hear your reaction to my thoughts (not to fan any flames, of course, but I feel that I can value your opinion as well).
Discussion Point 1
I too am a big proponent of KISS, and I find myself being the voice saying YAGNI at work during code review. However, it’s been my experience that defaulting to using DI almost never has a measurable downside.
In addition to being necessary (or as close to necessary as mathematically possible) to unit testing, building DI into the application from the beginning is certainly more conducive to extension when (not if, in my experience) necessary.
We create a web based suite of software for the medical industry. For various industry certifications, we have to record whenever a user accesses a patient record. An earlier version of our software may have done the following:
<?php
class MedicalRecordAccessLogger {
// User id to record as the accessor of the record
private $user;
public function __construct() {
$this->user = new User($_SESSION['userId']);
}
public function recordAccess($medicalRecordId) {
// Do stuff to record the access
}
}
Of course, this worked for several years and the Logger was able to be used in many different areas of the application (E-Prescribing, Treatment Planning, etc.).
Then, of course, Apple made the iphone and the ipad, and we had to create a mobile app. Then, we had 2 different places to check for the user’s Id.
Then, the world went crazy for RESTful API’s. Then we had 3 places to check for the user’s id.
Then, 3rd parties wanted to be able to tie in to our system to serve additional functionality to our customers (CRM, reporting, call center integration, etc.). 4 places.
Then, (yes, this far into the business being around) we realized we needed to implement unit testing. 5 places.
And there are user-based processes everywhere in the system.
Etc…
If, instead, from the beginning, all of the user objects had been injected into the AccessLogger, the act of creating a new interface to our system and being able to re-use all of our existing classes would have been as easy as writing a new User class implementation for the new interface and calling it a day.
Though we couldn’t see the benefit of enforcing dependency injection in those early stages of the product lifetime, the fact that we weren’t implementing DI at that time certainly caused a lot of additional work later. So, a few extra hours (at most) at the beginning of the project to save a few extra weeks later on sounds like a good trade off to me.
Therefore, I assert that there are no circumstances that would not benefit from from being Dependency Injected. Worst case scenario: your new functionality is likely guaranteed to be layer separated and testable (that’s enough reason for me right there).
Discussion Point 2
Here, I assert that not only does Dependency Injection NOT break encapuslation, but in fact will help ensure it is enforced:
In our circumstance, a patient is involved in most (though not all) transactions through our system. We need to have access to certain elements of a patient record for e.g. submitting insurance claims, scheduling appointments, etc. Though patient information is required for submitting an insurance claim, and that information is retrieved through one of our patient objects, all of the logic needed to generate the claim information is contained inside another set of classes. Those classes don’t concern themselves with anything about the provided patient object other than the fact that the patient object implements the ClinicalPatient
interface.
This is how the fact that our dependencies are injected helps enforce encapsulation. The ClaimGenerator
CANNOT perform it’s activities if it hasn’t been properly given an instance of ClinicalPatient
. Developers are free to swap out the dependency provided as they see fit, but there will not be an attempt to create claims if the ClinicalPatient
is not provided.
Within the scheduler, the fact that we have type-hinted a SchedulableResource
as a dependency of the Scheduler
object has allowed us to expand the functionality of the scheduling system to extend to things like resource scheduling (equipment, exam rooms, etc.) and employee related business activities. Again, the Scheduler
can only function if it’s configured correctly (by receiving a SchedulableResource
, and handles all of the activities needed to do the scheduling.