Dependency Injection Breaks Encapsulation

Show me a situation where DI is less preferable than another approach. In the case of singletons and service locators, DI is fewer lines of code and easier to implement. In the case of the new keyword in the constructor, DI is one extra line of code (although you need to instantiate both objects somewhere anyway so not any extra files to open…), but adds an incredible amount of flexibility ( http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/ ) … a worthwhile tradeoff in any case

DI is often the simpler solution.

edit: The only case where DI isn’t possible is where you don’t know the class name you need to instantiate, but that introduces a whole can of worms if you’re not using a factory method and the class has, or ever will have dependencies (It’s worse if the class doesn’t have dependencies and does after some time because then you have to find each instance of the new keyword!)

Any small program involving less than 5 or so classes and 500 or so code lines not including comments. Anything larger than that then it becomes increasingly hard to justify having no DI.

Thanks for the link @TomB , I’ll look into it. Those are good examples but not only. Legacy application, team members that don’t know how to do it (and don’t want to learn), languages that don’t support it out of the box - powershell, COBOL, C, … - (but this a php thread !), and certainly many other situations should be taken into account when applying any sort of abstraction.

I’ve seen so many cases where DI is done wrong, for instance: class D requires ILogger; class C requires D, class C requires B, class B requires A, so someone create the ILogger and cascade that dependency from B down to D, and before he knows we’ll have a constructor with 10 dependencies because the lower layers requires a bunch more of dependencies (here is where a dependency injector container/resolver comes into action, yet another abstraction). Actually I think this was more or less the point of @tony_marston404, but where he states Encapsulation, I state, “somehow”, the Segregation principle. In the example above, if I’m B using C (which requires D and its ILogger), why should I care for the ILogger? But then again there could be a place where I do care.
Also, If I don’t have the time to coach a junior, I prefer that he doesn’t do the abstraction than to implements the abstraction in the wrong way.

Again it is a matrix, know the tools and seek the one that best fits your needs. Just don’t do things blindly. But that is my point of view. And it’s like a but… everyone has one. :slight_smile:

But lets go back to the original question of this thread:

Does dependency Injection breaks encapsulation?

Well, in my opinion they complement, but it doesn’t break. Encapsulation doesn’t mean that a class/module/function/item is isolated from anything else. There is always some kind of dependency (a Class A, a Request, the String class, the array_push function, …) so, it a matter of balanced what assumptions of the class and what you need/think needs to be dynamic. And in that case, a dependency injection can be a great tool to resolve that situation.

But than again I’m not an “expert” in any of the principles and I might, and certainly am, wrong in my conclusion.

Your problem is that you seem to be aiming for the creation of perfect software which will always fail for the simple reason that there is no single definition of “perfect” which means the same thing to all people. That is why for every point of view held by one group of developers there will be another group with an opposite point of view. As it will be impossible to please everyone all of the time you therefore have to make a choice with the knowledge that whatever choice you make there will always be someone who will tell you that you have made the wrong one. I do not follow rules simply because they are there, I follow those rules which help me in my aim of developing cost-effective software. I decide which rules or principles I follow, not you, and I also decide on how to interpret and implement those rules. This may mean that I do not follow the same rules as you, or I do not follow the same interpretation of those rules as you, or I do implement those rules in the same way that you do, but that is MY choice. You are not entitled to tell me that I don’t have a choice.

Another problem with this relentless search for perfection or code purity is that it is just that - relentless and never ending. You can look at the same piece of code every day for a year, and each day you will think of another way to make it “better”, or to make it adhere to a newly invented principle, or a different interpretation of an existing principle. But what is the cost of this refactoring? Will there be any benefits made visible to end user? At some point you will fall foul of the Law of Diminishing Returns, but a wise man will stop before that point is reached.

Perfection may be an impossible dream, but “good enough” is easier to recognise, and always less expensive. The cost and effort in trying to achieve perfection was something that Craig Larman highlighted in his article “Protected Variation: The Importance of Being Closed (PDF)” which you can view at http://www.martinfowler.com/ieeeSoftware/protectedVariation.pdf in which he said the following:

That gives total justification to my decision NOT to implement DI in those circumstances where I deem the costs outweigh the benefits.

You also seem to think that all it requires to become a good programmer is the ability to follow the rules blindly and without question. This is where I have to strongly disagree. You can train a bunch of monkeys to follow a set of rules, but they will never be good programmers, just trained monkeys. Computer programming is an art, not a science, so unless you have the right talents to begin with you will never be a good programmer. To be a good programmer you must have a logical mind, the ability to analyse a problem and design a solution, as well as the ability to implement that solution using a structure that others can follow, understand and maintain.

That is not what I said. My application structure is based on a combination of the 3-Tier Architecture and the MVC design pattern. This means that I have four basic categories of component::

  1. Model - one for each of the 300+ database tables in my application
  2. View - one for HTML output, one for PDF and another for CSV.
  3. Controller - one for each of my 40+ Transaction Patterns
  4. DAO - one for each DBMS (MySQL, PostgreSQL, Oracle and SQL Server).

Each of my Model classes follows the Single Responsibility Principle in that it is responsible for the validation and business rules associated with a single database table. A lot of the things that you can do with a particular database can also be done with any other database table, so to make this common code sharable I placed it in an abstract table class which is then inherited by every concrete table class. It just so happens that, while developing complex database applications, I have discovered lots of different operations that I may need to perform on a database table, so all these operations, and the variables needed in their support, I have placed in the abstract table class so that they are there whenever I need them. Some of these operations may be used hundreds of times in an application, but others may only be used once, but that is irrelevant. They are operations which may be performed on a database table, so by the rules of encapsulation they MUST be placed in the same class.

If you have never needed a class which had to cover the same number of possibilities then it indicates that you have never written an application with the same level of complexity.

This is exactly the point which I have been trying to make. The problem is, how do you define “wisely”?

I disagree with your statement that “all other methods have some pretty severe negative effects”. This “severity” is just a matter of opinion, and some cases which you would class as “severe” I would class as “inconsequential” if not “irrelevant”. There may be multiple ways to solve a problem, and each solution will have its own set of pros and cons. It is up to the developer to weigh up the pros and cons of these solutions and to implement the one which he deems to be most cost effective. To say that the DI solution is all pros without any cons is plain wrong. I simply cannot justify the cost of implementing a flexible solution if I now that I will never need that flexibility.

I have already told you. Where I have only a single dependent object instead of a choice of several alternatives there is no justification for implementing a solution which allows me to switch to alternatives which will never exist.

Perfect is relative, “best practice” is not. Best practices exist for a reason and actively ignoring them when you have a choice not to results in poor software (like 9000 line classes with 10+ responsibilities)

This entire discussion is moot as your idea of “good code” is 9000 line classes with mixed responsibilities. If you can’t see why god objects are bad then discussing the nuances of DI and best practices is utterly futile. You’ve demonstrated your lack of knowledge. “Don’t create god classes” is OOP 101, if you didn’t learn that lesson, continuing this discussion is redundant until you do.

As Scott said, your credibility is shot, this discussion is over.

I have already shown you an example, and more than once.

I disagree. DI also has the side-effect that it fragments the code, which makes it harder to read, and therefore harder to understand and maintain.

I disagree. DI is only an effective solution when you have the problem for which DI was designed.

It is a point of view which I share, but which others in this discussion thin that I am not allowed to have.

If encapsulation is supposed to mean implementation hiding, which means that I should be able to call a method without any idea of how it is implemented, then surely by forcing me to define which dependent objects should be used in the execution of a method I am effectively un-hiding the implementation, which is breaking encapsulation? You may think that I’m stretching things a bit, but is that idea any more nonsensical than inheritance breaks encapsulation?

How many times must we go through this?? You showed me an example with a singleton, that was harder to follow and harder to understand. I had to look in the singleton class and the class that contained the singleton to work out what was going on. I also couldn’t easily work out what dependencies were needed. With DI I also need to look in two places, the place the object is constructed and the constructor itself. The difference is I immediately know exactly what dependencies any class has.

How long would it take me to look through a 9000(!) line file to work out what the dependencies are?? A lot longer than looking at it if had a single constructor that listed them that’s for sure.

I disagree. There is no single document labelled “best practice” or “standards” which is universally accepted by all programmers, not even all programmers using the same language. Different groups of programmers have their own ideas on what is best for them, but they have no right to enforce their personal ideas onto others.

[quote=“TomB, post:193, topic:113596”]
Best practices exist for a reason and actively ignoring them when you have a choice not to results in poor software.[/quote]
I disagree. It is possible to break or ignore any arbitrary rule and still write cost effective software. It is possible to follow a rule and still write crap software. Following rules blindly and without question does not make anyone a good programmer. That takes talent, and talented people know when a particular rule will not add to the value of their software and so can safely be ignored.

But it does not have mixed responsibilities. Each of my concrete table classes is responsible for the validation and business rules for a single database table. You cannot get any more singular than that.

And how does that description cover popups, buttons, workflows, databases, csv files, pdf files, etc, etc… If you can’t see that then you’re either blind or don’t understand what a responsibility is

How so? The singleton::gertInstance method returns an instance of the specified class. What more do you need to know? In my example an instance is obtained and consumed using two lines of code, one immediately after the other. What could be more simpler than that?

You shouldn’t need to know what dependencies are needed. You call an object’s method in order to do stuff, and it does it. How it does it should be irrelevant. That’s what implementation hiding is supposed to mean.

Each of those methods covers a situation where the Controller requires the Model to either change or check its state, therefore they belong in the Model. It would break encapsulation if I put them anywhere else.

$this->foo->bar()

what more do you need to know?

This is both shorter and easier to read than

$foo = singleton::getInstance('foo');
$foo->bar()'

Aha! Finally you get it! You’re the one who has all along been claiming this is an issue.

My point all along has been that you don’t need to know what the dependences are when using DI.

You are contradicting yourself. It is YOU who keeps telling that I MUST know all the dependencies required by an object because I MUST inject them. It has been ME saying “no, I don’t”.

sigh You’re saying it matters what the dependencies are. In the last thread you kept repeating that DI isn’t good because you don’t know what the dependency is when it’s consumed. Now you’re saying you don’t actually care what it is…

you missed my main point. How is

$this->foo->bar()

longer than

$foo = singleton::getInstance('foo');
$foo->bar()'

I have asked you repeatedly in both threads. When you account for the singleton class it’s longer and more complicated still. Posts which you keep ignoring.

This is all my opinion and, of course, is subject of being wrong and is certanly is, because I’m human.

Having different point of view is perfectly acceptable as long as you valid arguments giving a specific context. If they aren’t, admit them and learn for the next time.

@tony_marston404. For what I read so far, if your Person creates the Address because than that is your business assumption, that is your context, but make sure you have consistency. All business have assumptions. Write it down, make it a unit test, and have good reasons for it.

You asked me how do you define “wisely”. And my answer to you is, see the context, seek the different solutions and weight them, in some terms, like “development speed”, “knowledge”, “testability”, “maintenance”, “…” any of your functional and non-functional requirements. BTW, in terms of OO programming, having a SOLID architeture with Depedency Injector is always a item that I tend to seek in my “perfection”, in my “best scenario” given the context.

@TomB sorry to disagree on you, but “Best” is relative, is an adjective by it’s english definition. Nokia “used to be the best”, two years ago they were bought. I simply cannot say “They are the best”. If this was true, shouldn’t they still be “the best”. So, what is the context? In comparison with who? I can, however state that “I’m the best guy maintaining this app” (because there’s no one else that knows it), but I cannot say that “I’m the best developer in the world” (I’ve got flaws in here). So: “I’m the best but I’m also not the best”.
Another example, on the company that I work on, the stakeholders imply that we have CMMI Level 3 certification (because of its business) and in our implementation we need to follow a somehow solid waterfall (yeah, its deprecated I know! ), but still it is required for having that certification, its a business rule implied by the stakeholders, from who we get paid from. So, I must discover the business requirements, make the technical design and then, design the test over that technical design. I simply cannot use TDD in this scenario, so in my company “TDD isn’t a best practice”, it violates the rules.
Now, as principle, I never neglect SOLID and DI “best practices”.

Hope you understand my point of view, don’t get me wrong, I understand yours as a principle (a truth made my flawed humans like me :slight_smile: )

Surely you need to now what the dependencies are before you can inject them?