// What kind of Data Logic processes.
trait Data { ... }
// How Data reads from a concrete Dependency
class DataFromDependency extends Data { ... }
// Logic
class Logic(data: Data) { ... }
Whys:* Trivial testing of the Logic, by instantiating Data objects with fake data. If you ever had to instantiate a DB and populate it with 132 right objects just to test that date conversions work correctly, you know what this means.
* The Data adaptors reduce the semantic surface of the Dependency to what Logic needs. This makes reading and reasoning about Logic easier, especially if Dependency is a "fully featured" library with tens of methods Logic couldn't care less about.
I have nothing against Java and still take work in Java, but I think every Java developer would learn a lot from Ruby-like languages.
Like you, though, I struggle to find a clear message. Is this a complaint about things being done wrong? The solution presented around Dependency Inversion Principles was really tough to understand. It looks like he just made everything harder. It's been so long since I've done anything but front-end web work, though, that might just be me.
My hope was to make the differences more clear. Not accomplishing that was the more probable outcome ;)
(above is a special case of below)
http://en.wikipedia.org/wiki/Design_by_contract
Alas, Eiffel looked like Modula (Pascal), rather than like C/C++, so it never caught on. The important part, though, was the idea of class invariants + method pre-conditions + method post-conditions. Meyer's book, from years before Java and "Java beans" infected the group-think, talked about classes that explicitly were constructed in a valid state (which also meshes well with the practice of immutability), and methods with explicit conditions of their requirements and what they were guaranteeing they would accomplish.
"Java beans" pretty much took these otherwise sound engineering principles and pissed all over them.
Rather than having a constructor that builds an object that you can then start using, you have to guess (unless documentation is very good, which it won't be) which setters must be called before "bean" is non-crap.
Yes, I'm bitter that such an obviously flawed practice became standard operating procedure, to the point that doing things right is viewed as suspect.
By using constructor injection you prevent this from happening. Also, consider a case of refactoring. You have a class that is being created from a few places and add a new dependency via a setter to it. Compile your code, and it looks fine but actually isn't - you need to find all the places you create that object and provide the extra dependency. If you use constructor injection, you'd have a compile error (in a statically typed language) in all the places that need to be fixed.
If you use setter based injection (as opposed to constructor or parametric injection) then you also add the complexity of reasoning about when it can change.