4

I am trying to write code that uses Dependency Injection, both to allow mocking and to have a cleaner, more explicit design.

I often find myself coming up against a particular problem that I would have thought was common, but I have not yet found anything on the net that has helped me overcome it.

The problem is where object A depends on B, B depends on C, C depends on D, and so on in a chain that could have many many links in it.

It seems that to practise depedency injection here, A would have to ask for a B, a C, a D, etc in its constructor (or a BFactory, CFactory etc. for objects that create the instances on which they depend). I am supposing for the sake of argument that the dependencies are not optional or resticted to particualar methods, making setter injection or method parameter injection unsuitable.

This suggests to me that long chains of dependent objects are an antipattern. In an abstract sense it has something in common with the arrowhead antipattern. Long chains of dependent objects form an arrowhead-shaped sequence diagram.

Perhaps, then, I should avoid this practise, and follow the advice in "Zen of Python" that "flat is better than nested". This suggests a design where the main program creates two or three objects, which collaborate and produce a result that is returned to the main program, which then creates another two or three to do the the next stage of the work, and so on.

I have the feeling that this sort of code would be easy to comprehend and to debug, as well as making dependency injection easy. But it seems to go against the Tell Don't Ask principle, and make the main program too fat. I like the idea of main being so small and so obvious that it does not need unit testing. And Tell Don't Ask says to me that if everything starts with A and ends with A, then it is A's responsibility. Suppose A is a customer being billed, and the customer owns the data that is needed to start off the billing process as well as the email address that the invoice needs to be sent to right at the end. Then it seems that A should do the work itself (main could just call Customer.billYourself()) rather than handing reponsibility back to main by giving main a bunch of invoices and an email address.

So, should I be avoiding chains of dependency, to make DI easier, or embracing them because of Tell Don't Ask?

naomi
  • 1,934
  • 1
  • 14
  • 29
  • 1
    This is an interesting but _very_ hard question to answer without a concrete example to talk about. May be a bit more opinion than fact though, due to very varying design philosophies. – Joachim Isaksson Jan 06 '13 at 17:11
  • Your comment made me realise that all the examples I'm working with are in legacy code, which I'm refactoring for testability. If I posted them the general bad design would cause more distraction than clarification. I will try to think of a problem O' – naomi Jan 07 '13 at 00:10
  • 1
    OK so I tried to think of a situation where I'd be tempted to use long chains like that in a new app, and I couldn't think of one. Which may be because they ARE an antipattern and only arise in an app that has other problems. – naomi Jan 07 '13 at 00:29

1 Answers1

4

Classes have many dependencies. It's just a fact of life. But it's how those classes depend on each other that makes the whole thing good or bad.

You should read about the Stable Dependencies Principle. If you follow this rule, the number of dependencies shouldn't be an issue:

THE DEPENDENCIES BETWEEN PACKAGES IN A DESIGN SHOULD BE IN THE DIRECTION OF THE STABILITY OF THE PACKAGES. A PACKAGE SHOULD ONLY DEPEND UPON PACKAGES THAT ARE MORE STABLE THAN IT IS.

There are good dependencies, and there are bad dependencies:

Thus, we can say that a “Good Dependency” is a dependency upon something with low volatility. The less volatile the target of the dependency, the more “Good” the dependency is. By the same token a “Bad Dependency” is a dependency upon something that is volatile. The more volatile the target of the dependency is, the more “Bad” the dependency is.

Structure your app to have good dependencies.

Bob Horn
  • 33,387
  • 34
  • 113
  • 219
  • 2
    I understand this principle but I don't see how it relates to my questions. The problem is having to pass a C and a D into A's constructor when A does not directly collaborate with either but passes them on to B, who passes D along to C for C to use. Regardless of the direction of stability, doesn't it break encapsulation for A to have to know about D? If C stopped requiring D, A would have to change. Whether C requires D or not is an implementation detail that A should not have to care about. – naomi Jan 07 '13 at 00:42
  • Does A do anything with C? If not, then yes, it breaks encapsulation and it's bad to have one class depend on something to just pass it to another class. Instead of constructor injection, you could use a DI container and have each class get what it needs from there. – Bob Horn Jan 07 '13 at 00:54
  • Thanks - that makes sense. (And my own previous comment doesn't, now that I think about it.) Sadly I can't use a DI container on this project. But I think it could be done with factories. If A needs C to create a B with, then A could ask for a BFactory and the BFactory could start by making a C. Not as slick as a container but still encapsulated and mockable ... – naomi Jan 07 '13 at 01:24
  • Cool, sounds like you ended with a good solution. If my answer didn't help, I can just delete it. Good luck! – Bob Horn Jan 07 '13 at 01:39
  • 1
    Hmm, not sure if problem won't just re-emerge in the form of FactoryFactoryFactories ... Anyway no need to delete, that C++ Engineering series could use a heads-up. – naomi Jan 07 '13 at 07:16
  • In any case, my question is basically a duplicate of this one: http://stackoverflow.com/questions/2425458/how-to-do-manual-di-with-deep-object-graphs-and-many-dependencies-properly – naomi Jan 08 '13 at 10:31