Service location for convenience

107 views Asked by At

I have 2 questions regarding the following code snippet:

public class Provider
{
    private static Provider _instance;

    /* Internals ommited. */

    public static Provider Instance
    {
        get { return _instance ?? (_instance = new Provider()); }
    }
}

public class Consumer
{
    private readonly Provider _dependency;

    public Consumer()
        : this(Provider.Instance)
    {
    }

    public Consumer(Provider dependency)
    {
        _dependency = dependency;
    }

    /* Internals ommited. */
}

1) The second constructor uses the dependency injection pattern, what is the first one doing? Service location?

2) Is it common practice to provide such an overloaded ctor which uses a default instance? Or are there better ways to do something like this? (I would like to avoid implementing a wrapper class).

2

There are 2 answers

2
Chris Mantle On BEST ANSWER

1) The first constructor is using static dependency injection. This pattern allows classes derived from Provider to be injected for testing purposes, or as an alternative to the default Provider implementation at runtime. Provider itself may be a service locator - that depends on what objects or services it actually provides.

In this example, though, Provider is injected into Consumer. That's dependency injection, not service locator (which requires a class to determine it's own dependencies, causing close coupling between them). Thanks to the dependency injection, it doesn't matter to the Consumer class if it gets the default implementation of Provider, or a class derived from it. The classes could be more loosely coupled if a) Provider was an interface, and b) if client code always determined which instance to inject, instead of having a default.

2) Personally, I think static providers are a code smell, but they do work, and may be completely appropriate in your code-base. Ideally, Provider should be a singleton factory injected by a dependency injection framework. We have static providers in the code-base I work on at the moment, and I'm slowly replacing them in this way. Consumer should have a single constructor - the second one.

1
Jon On

The second constructor uses the dependency injection pattern, what is the first one doing? Service location?

Assuming that Provider is the dependency itself and not some kind of registry then no, it's still dependency injection. You are just providing a default value for the dependency for convenience.

Is it common practice to provide such an overloaded ctor which uses a default instance? Or are there better ways to do something like this? (I would like to avoid implementing a wrapper class).

It's definitely OK, but you might want to consider a factory method as an alternative to a default constructor:

public static Consumer CreateDefault()
{
    return new Consumer(Provider.Default);
}

This way you avoid the possibility of your clients glossing over the fact that the default constructor makes a (possibly important) choice on their behalf.