The Dependency Inversion Principle in the Wild
martinfowler.com
martinfowler.com
It just seems to me that "software architects" see the world in abstractions. Another friend calls this world view "enterprisey".
Fowler and I would likely agree on the principles of reducing the cost of change, DRY, YAGNI, etc.
But we wouldn't agree on the mechanics for achieving those goals.
My thesis is that some people truly think that way. What I find to be obtuse, obfuscated, tortured, overwrought is apparently pure poetry to others.
On the flip side, I know that how I work drives many other people absolutely nuts.
Your code should not deal with things at a lower level of abstraction than itself. External libraries tend to expose lower level interfaces than you need (because the API needs to be flexible enough to deal with a variety of use cases) , so build an abstraction in the middle between your code and the library that gives you exactly the functionality you need and nothing more.
[1]: http://www.jamesshore.com/Blog/Dependency-Injection-Demystif...
Articles like the dependency injection one are why I shy away from the Java community; pedantry about simple concepts that should be obvious good design indicate to me that the point really needs to be pounded home in the community due to widespread bad design. It can be nice to have terms to refer to different practices, but it just seems too much to me. That said, I know lots of good developers who use Java a lot, and I guess it's a good way to get a lot of mediocre programmers to make maintainable code.
Agree.
Please note the cultural context. Java, XML, design patterns, and enterprise (CORBA, MQ) all hit about the same time. So there was a temporal grouping for all those things.
I started a design patterns study group about 15+ years ago. (It continues to this day. We've covered many other topics over the years, including SICP, concurrency, HTML5, computational intelligence, etc.)
There was no bigger fan of "design patterns" than me. Now, I think learning "design patterns" are a phase of a developer's maturity. Kind of like only a master jazz musician knows how to break the rules of jazz.
Unfortunately, it's hard to expunge (deprecate) bad ideas. For many, it's simply easier to get a fresh start by adopting a new language, tool stack, community.
Java is a pretty good language. What it needs is a do-over. What would Java look like today, following the original principles of the Oak Team?
Have a look at more modern Java; for example Acteur[1]. Guice and a focus on annotations have dramatically reduced the annoying boilerplate that used to surround EJB. Spring appears to be on the way out, which I think everyone can be thankful for - although the latest versions of Spring are starting to become quite good now, so maybe it will survive.
Plus with the way Android is going, if you want to write something that actually runs on the devices you own, you'll want to know Java. ;)
"Dependency Injection is great. For the 6 to 8 classes you need to wire up! In which case, why would you need a framework?"
To me they seem related as in both cases you're ensuring that each part of the code only has to deal with functionality it directly cares about, through interfaces that ensure that it isn't overloaded with extra information.
(I hate the name though. Because that's never clicked with me at all.)
Lets say I was going to ship something building on what this article talks about. Instead of interacting directly with my shipping objects (Fedex, Usps, etc), I'd write a common interface for them (ShippingGateway). In addition Id need so sort of mapper for each shipping object that would translate my ShippingGateway calls to the respective object (FedexMapper etc).
My code would eventually look like:
shipping = new ShippingGateway(...)
estimate = shipping.estimate(address, 'fedex', ...)
and that should never have to change only the mappers and objects themselves would. What am I missing?Your code wouldn't create it's own ShippingGateway - it would be passed in (or "injected"). That Shipping Gateway could be a RealShippingGateway (for when the application is running in production) or a MockShippingGateway (for unit testing), or some other variant for when your test users don't want to actually ship things. It doesn't matter which of those it is to the object it's passed into, all it knows is that it has a ShippingGateway, and when/how to call it.
So it's Adapter Pattern + Interfaces together that matter.
Now look at the arrows in the DIP example. There is an inverted arrow by comparison to the gateway diagram. This is because you have inverted the dependency so that your high level code no longer depends directly on the implementation.
2. Everything used in the same place should use the same set of concepts (aka "level of abstraction", even tho abstractions can't always be split into clear levels).
So instead of your code knowing how to do something simple like create and use a database connection, you pass it a wrapper around a database connection and have it know how to use (but not create) that instead.
I'm not entirely clear on where the inversion-of-control (framework vs library) part fits in tho...
The comment on zeroed setInterval's clearing the stack were appreciated though, something I'll keep in mind.
I wasn't confused by the rest because I didn't attempt it.