Dependency Injection Breaks Encapsulation

There is no rule that says that a class cannot contain more than N methods, or that a method can only contain N lines of code. In fact the rule of encapsulation says quite explicitly that ALL the methods and ALL the properties for an entity SHOULD be contained in a single class for that entity.

As for the mixing of responsibilities, your definition of “responsibility” must be different from mine. The class you are complaining about is not a concrete class but an abstract class, and contains all the methods and properties required to deal with an individual database table. There is no direct communication with any DBMS as that is handled in a separate object. There is no creation of HTML or PDV or CSV output as that is handled in a separate object.

I have told you repeatedly that Dependency Injection was designed for those situations where a dependency can be supplied from a number of alternatives, so where I have a number of alternatives I am quite happy to use DI. But in those circumstances where a dependency can only be supplied from a single object without any alternatives then using DI is a waste of time.

I have provided code samples showing where I do use DI as well as a sample where I do not use DI, so I am not going to waste my time in duplicating that effort.

No, you have not. Where you do/not use something is irrelevant. You need to demonstrate how one is better than the other and explain how you’re measuring “better” something you have spent 500+ posts avoiding.

http://misko.hevery.com/code-reviewers-guide/flaw-class-does-too-much/

No, it is not wrong. If there are 1 thousand pages on the web labelled “best practices”, but they contain contradictory information, then which of those thousand pages do I choose to follow? Just because I do not follow the same documents as you does not make me wrong, just different.

I disagree. The topic under discussion here is Dependency Injection, and if there are any circumstances where DI is not a good solution. I have already explained many times the original purpose of DI which was to deal with the case where a dependency can be supplied from a number of alternative objects. Where I have a number of alternatives I use DI, but where I do NOT have a number of alternatives I do NOT use DI. I certainly do not have to prove that the code which I use to NOT implement DI is superior to any code you can dream up which DOES implement DI. I use DI where it is appropriate, but I do not use DI where it is not appropriate.

[citation needed]

Again, you’re not backing anything you say up with anything tangible. Best practices exist and the fact that professionals, academics and the authors of languages independently come to the same conclusions about what is a good way of working and what is a bad way of working adds weight to the arguments.

Just because you choose to avoid/misunderstand best practices doesn’t mean they shouldn’t be used and it certainly does not mean you should advocate their avoidance.

Not that it’s even relevant. Let’s ignore that for now. The argument best practices advocates are making is "X is bad because it causes Y, Here’s an example of method 1, here’s an example of method 2, method 1 is better beacuse… " Your response to this is “No it’s not”.

Arguments for best practices are backed up with reasoning and code examples comparing the different approaches. Your responses are not.

I feel like we’re going in circles here. Has it not been stated that Tony does use DI, when he feels it is appropriate to his application? And then chooses to not use it, when he feels it isn’t?

Hasn’t it been said that DI can provide benefits and efficiency to an application?

Has the actual question been answered? “Dependency Injection Breaks Encapsulation?” Probably not. Do I see us ever getting down to that answer? No. Do I feel that both of you have made your same points roughly 15 times now? Definitely.

As much fun as it is to see the bantering back and forth, let’s call it what it is and let it be. The two of you will never get on the same page and that’s life. Let it go. The rest of us have.

8 Likes

You obviously did not read what I wrote. None of my objects which is based on this abstract class constructs any HTML, PDF or CSV output as those functions are handled by totally separate objects. My table classes simply hand over any data to one of those objects, and those objects construct the output in the relevant format.

In this article http://blog.8thlight.com/uncle-bob/2014/05/01/Design-Damage.html Robert C Martin had this to say

[quote]
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. [/quote]
Note here that what he is saying that GUI, business rules and database access are three responsibilities which should not be mixed in the same class. That separation is PRECISELY what I have achieved in my framework as I have separate classes for GUI, business rules and database access. I designed my framework to implement the 3 Tier Architecture, and this achieves precisely that level of separation.

What you’re doing in your own code is 100% irrelevant to anyone but yourself. Provide reasoning for your arguments along with minimal code examples.

I’m not burning my eyes out looking through that 9000 line class again but it’s clear it’s doing more than that. What exactly to “buttons”, “workflows”, “popups” and various other things it has methods for have to do with interacting with a database table?

But before you answer that, please give us some code examples for this:

The problem is, I’ve spent around 500 posts asking tony to provide some code examples and he keeps avoiding the question.

In post #2 I asked for:

And I’m still waiting.

And you will be forever. That’s the point. You are proving the definition of insanity.

6 Likes

Well, I wouldn’t call Tom insane. It’s more like Tony is the epitome of the old adage…

…you simply can’t teach old dogs new tricks.

Scott

Tony ought to be a politician. He appears expert at NOT answering the question that has been asked and side tracking it into a “broken record” answer that is irrelevant to what was asked.

OK, guys, lets not get personal again.

1 Like

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.

2 Likes

I 100% agree with this with one small caveat. I’ve been trying to get Tony to demonstrate a measurable downside for this entire topic. The caveat is: when you don’t know up front exactly what class you’re going to instantiate: new $whatever then DI is not a direct solution. Although as I said before, this opens a can of worms because you’re assuming, as Tony is in his example, that each potential class has the same constructor arguments, which is a huge sacrifice of flexibility/scalability. (The answer is a factory but that’s beyond the scope of this discussion at the moment)

Very well put. I think for me the fact that there are no measurable downsides of using DI and every other approach has measurable downsides makes DI a no-brainer even if you think you’ll never want to swap out the implementaiton.

This is a good example of how DI gives you incredible flexibility. Although you may not need something now it’s often likely that you’ll need code to do something very similar in the future and although introducing features that aren’t necessary is actively detrimental (more scope for bugs, more development time, etc) the cost of implementing DI is at worst zero (or very near zero) and at best, gives you many benefits when it comes to testing.

As well as insane he is also blind. I have previously supplied a code sample where I do not use Dependency Injection, yet he still wants more.

Tom isn’t asking for code that doesn’t use dependency injection. If that was your understanding, then it is no wonder why we are going in circles.

Tom is asking for code, a pattern, which demonstrates what DI can do, with the same or even more advantages and with the same or less disadvantages. Your answer clearly doesn’t do that and he also pointed that out too.

The answer is, currently, there isn’t any other pattern better than DI and thus, the point is, DI is a great pattern for many reasons, with only minor disadvantages and why you see it everywhere. It is NOT evil. It is very useful.

You tend to jump on those rare disadvantages (some incorrect like breaking encapsulation) and make a mountain out of a molehill, all in the attempt to prove your lack of usage of DI is OK, whereas, nobody ever actually said, it isn’t ok. You came to that conclusion. All this is simply you being all defensive about your framework’s lack of use of DI “everywhere”. Nobody said you MUST use DI everywhere and all of the time. Pretty much everyone is saying it is a great pattern, so you SHOULD use it everywhere and all of the time, especially… if you want to have a modular and quite complicated and yet still testable code base.

Scott

1 Like

Although I am not actually injecting an object I am injecting the name of a class from which an object can be instantiated. My implementation may be unusual, but the principle is the same.

I disagree. Where each of my reusable controllers requires a dependency I am supplying the name of that dependency from outside. This follows the description of DI more than it does IoC.

What has the number of constructor arguments got to do with DI?

Explain “almost never”.

There are two approaches here

  1. Always employ a particular design pattern or technique unless you have a good reason not to.
  2. Never employ a particular design pattern or technique unless you can prove that it has benefits.

@TomB appears to be in the first camp while I am most definitely in the other. As far as I am concerned each design pattern or technique supplies a possible (not the only) solution to a particular problem or set of circumstances, and if I don’t have that particular problem I see no need to implement that solution.

When it comes to DI my view is that it only provides a benefit when a dependency can be supplied from a collection of alternative objects, such as where each of my 40 Controllers can be supplied with the name of any of my 300+ Model classes. But where a dependency can only be supplied from a single object where there are no alternatives, such as where my Person object calls my Address object, there is absolutely no need for DI at all. In fact as well as not providing anything of value it has the effect of fragmenting the code and making it more difficult to read and therefore maintain.

I disagree. I have solved the problem similar to the one you have described without the use of DI, so as far as I am concerned DI is not necessary. It may provide a possible solution, but it is not the only solution.

I disagree. Encapsulation has nothing to do with dependencies, only methods and properties. The code behind each method is hidden, which leads to the concept of implementation hiding (which is not to be confused with information hiding). When used appropriately - when a dependency can be supplied from a number of alternative objects - then DI has distinct benefits. But when DI is used inappropriately - when a dependency can only be supplied from a single object without the possibility of any alternatives - it is my opinion (and this opinion is shared by others) that your are exposing the implementation and therefore breaking encapsulation.

Explain to me why this is not a solution? Each of my Controllers can operate of any of my 300+ Model classes, so for a particular user transaction I inject the name of the class into the Controller. The fact that I don’t inject a fully-instantiated object is a minor detail - the significant point is that none of my Controllers has any hard-coded Model names.so that they can work with any Model in my application.

As none of my Model classes requires any constructor arguments I do not see this as a problem.

That is your opinion, but unfortunately it is not an opinion which I share.

Apart from the fact that implementing features that you don’t actually need today is in violation of YAGNI, your idea that implementing DI has zero cost is not one that I can accept. When used inappropriately it fragments the code and makes it harder to read, harder to understand and harder to maintain.