Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency Injection - What to do when you have a lot of dependencies?

I have a class A that depends on 10 other classes. According to Dependency Injection pattern, i should pass all dependencies of A by its constructor.

So lets assume this constructor (of course this is not a working or real code, since I am not allowed to post the real code here)

public ClassA(ClassB b, ClassC c, ClassD d, ClassE e, ClassF f, ClassG g, ClassH h, ClassI i) {
  this.b = b;
  this.c = c;
  this.d = d;
  this.e = e;
  this.f = f;
  this.g = g;
  this.h = h;
  this.i = i;
}

I have read on Martin Fowler's book about refactoring that having a method with a lot of parameters is a code smell and should not happen.

My question is: is this OK when we are talking about DI? Is there a better way of inject dependencies without breaking Martin Fowler's rules?

I know I could pass the dependencies through properties, but that may cause errors since no one is really sure what should be pass in order that the class works.

EDIT

Thanks for all your answers. I will try now to demonstrate some of class A dependencies:

1 - A class to access a DB
2 - Another class to access another DB (yes, i need to perform operations on two databases)
3 - A class to send error notifications by email
4 - A class to load configurations
5 - A class that will act as timer for some operations (maybe this one can be avoided)
6 - A class with business logic

There any many others that i am trying to get rid of, but those are really necessary and I dont see any ways of avoiding them.

EDIT

After some refactoring now i have 7 dependencies (down from 10). But I have 4 DAO objects:

CustomerDAO
ProcessDAO
ProductsDAO
CatalogDAO

Is it correct do create another class called MyProjectDAO and inject those DAOS onto it? This way I will have only one DAO class that aggregates all DAO objects of my project. I dont think this is a good idea because it violates the Single Responsibility Principle. Am I right?

like image 395
Rafael Colucci Avatar asked Mar 13 '12 14:03

Rafael Colucci


2 Answers

In my experience:

  • Try to design your class so it needs fewer dependencies. If it needs that many, it may have too many responsibilities.
  • If you're really convinced that your class design is appropriate, consider whether it may make sense for some of those dependencies to be joined together (e.g. via an adapter which takes responsibility for one "big" operation your class needs by delegating to a few of the dependencies). You can then depend on the adapter instead of the "smaller" dependencies.
  • If every other bit really makes sense, just swallow the smell of having a lot of parameters. It happens sometimes.
like image 97
Jon Skeet Avatar answered Oct 05 '22 10:10

Jon Skeet


Can you justify (to yourself) why the class depends on 10 other classes? Are there member variables you use to tie together a subset of those classes? If so, that indicates that this class should be broken up so that the extracted class would depend on the subset and the variables that tie such state together goes in the extracted class. With 10 dependencies, it's possible that this class has simply grown too large and needs to have its internals broken up anyway.

A note regarding your final sentence: such order dependency can also be a code smell, so it's probably good not to expose it in your interface. In fact, consider whether or not the order requirements are because operations need to be carried out in a specific order (it is the complexity of the algorithm or protocol), or because you've designed your classes to be inter-dependent. If the complexity is due to your design, refactor to eliminate the ordered dependency where possible.

If you cannot refactor (the complexities are all essential and you just have a terrible coordination problem on your hands), then you can abstract the ugliness and keep users of this class shielded (builder, factory, injector, etc).

Edit: Now that I have thought about it, I am not convinced that essential complexities of your algorithm or protocol cannot be abstracted a bit (though that might be the case). Depending on your specific problem, similarities in the manipulations of those dependent classes might either be better solved with the Strategy pattern or the Observer pattern (event listeners). You might have to wrap these classes in classes that adapt them to slightly different interfaces than what they currently expose. You'd have to evaluate the tradeoff of having the code in this monster class become more readable (yay) at the expense of up to 10 more classes in your project (boo).

I'd also like to make an addendum to abstracting the construction of this class. It seems important that any class that depends on this class also use the Dependency Injection pattern. That way, if you do use a builder, factory, injector, etc. you don't accidentally rob yourself of some of the benefits of using the DI pattern (the most important in my mind is the ability to substitute mock objects for testing).

Edit 2 (based on your edit):

My first thought is "what, no logging dependency?" :)

Even knowing what the dependencies are, it's difficult to offer useful advice.

First: what are the responsibilities of everyone? Why does this class depend on controller code (the business logic) and on Model code (two different database access classes, with DAO classes)?

Depending both on DAOs and DB access classes is a code smell. What is the purpose of a DAO? What is the purpose of the DB classes? Are you trying to operate at multiple levels of abstraction?

One of the principles of OO is that data and behavior get bundled into little things called classes. Have you violated this when you created this business logic class distinct from the objects it manipulates distinct from the DAO distinct from this class? Related: Take a brief diversion into SOLID.

Second: A class to load configurations. Smells bad. Dependency Injection helps you identify dependencies and swap them out. Your monster class that depends on certain parameters. These parameters are grouped into this configuration class because...? What is the name of this configuration class? Is it DBparameters? if so, it belongs to the DB object(s), not to this class. Is it generic like Configurations? If so, you've got a mini dependency injector right there (granted, it is probably only injecting string or int values instead of composite data like classes, but why?). Awkward.

Third: The most important lesson I learned from Refactoring was that my code sucked. Not only did my code suck, but there was no single transformation to make it stop sucking. The best I could hope for was to make it suck less. Once I did that, I could make it suck less again. And again. Some design patterns are bad, but they exist to allow your sucky code to transition to less sucky code. So you take your globals and make them singletons. Then you eliminate your singletons. Don't get discouraged because you've just refactored to find that your code still sucks. It sucks less. So, your Configuration loading object may smell, but you might decide that it isn't the smelliest part of your code. In fact, you may find that the effort to "fix" it isn't worth it.

like image 24
ccoakley Avatar answered Oct 05 '22 08:10

ccoakley