views:

46

answers:

2

This is a question about dependency injection. When constructing a service object, we pass in collaborators via the constructor in the construction phase. The service object would implement an interface, and that would be called during the run-phase.

Sometimes is is difficult to know if a particular object should be passed via the constructor or be part of the interface implemented by the service class?

Are there any rules about choosing one option over the other? The question is most difficult when you know the interface is only going to be called once in the scenario you are coding for.

+2  A: 

a rule of thumb I often use is whether the class can function without the value being passed in, balanced with the the complexity of the constructor. If the class cannot properly operate without the argument, it's usually good to put it in the constructor. On the other hand, if the class is designed to do something that requires additional work, like accept connections over a socket, such work should usually be delayed to a later function.

atk
+5  A: 

I like to think of it like this:

  • Constructor arguments are implementation details
    • They are scoped to all operations
    • They do not change in response to any operation (invariant)
    • The interface can be understood without them
    • They are configuration values which reflect an application's seams
  • Method arguments are contextual
    • They are scoped to an individual operation
    • They are runtime values which reflect an application's data flow

A lot of the art is in framing the problem correctly. For example, we might say to ourselves "I need to create a new row in the user table." From that perspective, either of these signatures seems fine:

void Insert(User user);

void Insert(User user, IDbConnection dbConnection);

However, we can break down our task definition:

Intent: Create a new user

Implementation detail: A user is a row in a table

Let's instead frame the task as "I need to create a user". This gives us a way to evaluate the two signatures above, favoring the one which matches our intent:

void Insert(User user);

Analysis of an operation's intent and the applicable scope of its data generally gives solid results.

Bryan Watts