You missed my point though, you can’t use that as an argument against DI. I agree with you and separation of concerns is a very big topic in in itself but you’re describing a problem with the initial code then comparing a solution to that problem to the problem itself, not a solution to the problem with another solution to the problem which is the only way to get a fair comparison.
The question here is “Would this code be improved by separating out the display logic?” the answer in most cases will be “Yes”. So the question you now need to ask, if you have determined that the code would be improved by separating out the display logic then the question becomes “Which method of doing that is preferable?” not, as you are, “Is this solution to the problem more code than the original problem”.
The difference is subtle but important. If you have determined that you need to separate out the display logic then you can look at different methods of doing that, but conflating the initial problem with one possible solution is not helpful in this discussion.
This is true but it’s not really relevant, you don’t generally hard code primitives, you use variables which are set in the constructor. Saying “The first argument is an Integer” is no different than saying “The first argument is an Address object”. There’s no difference here. And on-topic, I can define an argument as a float and accept an integer, I just need some kind of numeric value, most programming languages will allow this basic form of primitive polymorphism by doing the conversion for you. PHP and other loosely typed languages do this to a greater extent, I can pass “123” as a string into an argument that requires an int and it will just work.
I’m not sure I understand this. If you get a bug that exists only in production then your testing is sub-par but that’s a whole different issue.
As I said before, if someone is complaining that they passed an invalid argument it’s their own fault. You’re essentially saying people are complaining when they do this:
And then complaining when it breaks which is clearly user-error.
If you’re providing a library for others to use, loose coupling is even more important because unless the requirements for their project are exactly the same as yours then they won’t be able to use your library. If they can inject the dependencies themselves, they can use at least some of your code.
But by the same token in reality and out side these theoretical examples requirements change often. Let’s take the code you quoted:
new J(new I(new H(new G(new F(new E(new D(new C(new B(new A())))))))));
And imagine the non-DI version that J create H, H creates G, etc:
Then the constructor for A changes to need a text file location. This has to be provided by the application developer not the library author as hard coding it in the library presents all kinds of portability issues.
So the only way to do this is alter J to have a constructor argument:
which then passes it to I that passes it to H that passes it down to G etc, etc.
The problem again is the lack of atomicity in this discussion. Rather than being able to discuss the problem at hand it keeps getting pulled back to “What if this happens, what if this happens”. Let’s talk about the pros/cons of DI before discussing further concepts such as containers otherwise we are clearly crossing concerns and conflating different (albeit related) problems.
There are two different discussions happening here. The initial topic was clearly centred around what constitutes well designed software in relation to DI and the reasons for that but we keep straying into anecdotes and unrelated or loosely related topics.
As an analogy: I am trying to discuss the merits of different CPUs. I’ve stated my metric as being “I want the fastest one available for a specific task” so I look at the CPUs analyse them according to my stated criteria and you and tony keep saying “Well what about this motherboard” “Doesn’t this case look pretty?”, “Does it have to be silver??”, “That won’t work with my RAM”, “I have this processor and it works just fine for me”… all I want to do is directly compare processors.
Now, It would be fine to come along and look at them from a different metric and say “I want the cheapest one available” but both Tony and yourself keep failing to identify the metric you are using to compare the different approaches. We’ve briefly skirted around lines of code/development time but as we’ve seen, this isn’t very clear cut as all your examples are mixing in other details (SoC, DI containers, etc).
To reiterate, the metric I am using is essentially the list I posted earlier:
As “Best” in programming tends to mean flexiblity, and the list here is quite good http://c2.com/cgi/wiki?GoodArchitecture :
Robust - lacking bugs and tolerant of external faults
Maintainable - easy to maintain and extend
Useful - utility, beyond the immediate need (due to flexibility and extensibility)
Scalable - ability to grow in capacity, not in features
Common Vision - direction, strategy
Agile - simple and “elegant” enough to refactor easily; flexible
Extensible - ability to grow in features or in depth
Responsive - performance now and after adding features or expanding scale
Edit: And if you do identify another metric. We then need to ask the question: at what point does that metric become important?