views:

98

answers:

4

If I am writing up a class with more than 1 constructor parameter like:

class A{
    public A(Dependency1 d1, Dependency2 d2, ...){}
}

I usually create a "argument holder"-type of class like:

class AArgs{
    public Dependency1 d1 { get; private set; }
    public Dependency2 d2 { get; private set; }
    ...
}

and then:

class A{
    public A(AArgs args){}
}

Typically, using a DI-container I can configure the constructor for dependencies & resolve them & so there is minimum impact when the constructors need to change.

Is this considered an anti-pattern and/or any arguments against doing this?

A: 

My only concern is that your dependency holder class AArgs may disguise the fact that you have a large number of dependencies, and thus that the class you're constructing perhaps has too many responsibilities.

If your dependency holder class AArgs typically has few members (say, 3 or less) then that's not a problem.

Brian Agnew
Although I have usually ended up with 2-3 members for AArgs does it really matter that there are more than 3? I mean, is it sin to pass all dependencies of a class into the constructor if it does need them (without breaking the Single-reponsibility principle). Not that I encountered this before - would love to hear a real-world example
Sunny
Well, 3 is an example number. If your parameters are simple config parameters then that's not a problem. I have more concerns if they are complex objects with behaviour. That would point to the containing object having multiple responsibilities.
Brian Agnew
+4  A: 

This does open the door for it appearing that your dependencies are optional. By having properties for each of the dependencies for your AArgs class, someone can think they only need to fill in Dependency1, and everything will work as expected.

By explicitly listing all of your dependencies individually, you give a clear contract about what is needed for your class to function. If you have so many dependencies that the constructor is cumbersome, that should be considered a smell, as your class may be doing too much.

Pete
I agree to the "clear contract" argument, but then if I need to make a change, I might end up with overloaded constructors in which case I will end up passing so default values to the other constructors too which over a period of time just adds up in terms of maintenance...
Sunny
Why do you need overloads? Are there optional dependencies? If so, you can consider using property injection for those. Then it is clear which are optional and which are required dependencies. Other classes that consume class A should be having A injected anyways, so they shouldn't have to worry about he constructor for A either way.
Pete
Sunny
Sunny
@Sunny: Very true. The "..." in AArgs in your example doesn't quite fill in the whole picture about what AArgs has for constructors, etc. That being said, I think class A will be more "intention revealing" if it uses explicit constructor and property inject directly instead of relying on AArgs. I personally would consider AArgs to be a smell, but not something that is *wrong*, per se.
Pete
I don't see how the dependencies could appear optional, as the properties are read-only.
Mark Seemann
+2  A: 

Mark Seemann has blogged about this.

You are passing in an aggregate of dependencies, instead of each dependency by itself--basically it's a refactoring. Mark argues that by doing this, you are making things in your domain explicit which were previously implicit.

dan
Excellent article, thank you. This is what I am trying to get to, but wanted to know if there any reaons not to go for it...
Sunny
@Sunny: note that the aggregate service is far more than an object that simply holds a set of dependencies for consumption.
Pete
Sunny
Pete has a good point. Making an Parameter Object is a potential violation of the Law of Demeter, so if you can, see if you can encapsulate the behavior into it as well.
Mark Seemann
A: 

Well I wouldn't go so far as to call it an anti-pattern, but if a developer needed to make changes to your code base it would be confusing to figure out what the constructor needs, so "Just to be Safe" they could just pass all the dependency objects into the constructor. Yikes! Its one of those abstractions that could be really abused.

Anthony
Sunny