Search code examples
dependency-injectioninversion-of-controldefault-valueservice-locator

Default values for constructor arguments in a library project


I am writing a library that will provide a collection of public types to its consumers.

I want to make types from this library dependency injection friendly. This means that every class needs to have a constructor through which it is possible to specify every single dependency of the object being initialized. I also want the library to adhere to the convention over configuration principle. This means that if a consumer wants the default behavior, he may use a parameterless constructor and the object will somehow construct the dependencies for itself.

In example (C#):

public class Samurai {

    private readonly IWeapon _weapon;

    // consumers will use this constructor most of the time
    public Samurai() {
        _weapon = ??? // get an instance of the default weapon somehow
    }

    // consumers will use this constructor if they want to explicitly
    //   configure dependencies for this instance
    public Samurai(IWeapon weapon) {
        _weapon = weapon;
    }
}

My first solution would be to use the service locator pattern.

The code would look like this:

...
public Samurai() {
    _weapon = ServiceLocator.Instance.Get<IWeapon>();
}
...

I have a problem with this, though. Service locator has been flagged as an anti-pattern (link) and I completely agree with these arguments. On the other hand, Martin Fowler advocates use of the service locator pattern exactly in this situation (library projects) (link). I want to be careful and eliminate the possible necessity to rewrite the library after it shows up that service locator really was a bad idea.

So in conclusion - do you think that service locator is fine in this scenario? Should I solve my problem in a completely different way? Any thought is welcome...


Solution

  • If you want to make life easier for users who are not using a DI container, you can provide default instances via a dedicated Defaults class which has methods like this:

    public virtual Samurai CreateDefaultSamurai()
    {
       return new Samurai(CreateDefaultWeapon());
    }
    
    public virtual IWeapon CreateDefaultWeapon()
    {
       return new Shuriken();
    }
    

    This way you don't need to pollute the classes themselves with default constructors, and your users aren't at risk of using those default constructors unintentionally.