Constructors for services are a really handy way to pass in configuration, variation and dependencies. Here are some ways I have learned to use Constructors and some ideas on testing.

What shouldn't be in a Constructor

Execution. Any complex execution of code should not be in a constructor. This includes asynchronous method calls (which either has to block synchronously, or runs off to nowhere), web requests, and complex calculations.

Why? Why not run your whole application in a constructor? Well, when you are reading code, you wouldn't expect much work to be done in a constructor. So the reality wouldn't match your expectations. Stick with the conventions of the language so that you do not surprise the reader of your code. Another reason is that the object initialisation phase may happen outside of your application code. If you are following IOC or using a DI container, you may find you have no control over the construction of your objects, where it runs or what gets provided.

Parsing.
You probably also want to keep complex parsing out of constructors too. Don't pass in a string and deserialise as XML or json, but instead parse first, and then pass in the resulting data object. This will make it easier to use and test parsing separate from the responsibilities of this class.

Constructors. What else shouldn't be in constructors? Well, potentially other constructors. If you want to adjust how that object is constructed, or use the liskov substitution principle to swap dependencies out for different implementations, you would be stuck. This is less of a strict rule, and I will cover the exceptions to it later on.

So what should?

Constructors work well having the general dependencies(other services) of the object passed in (if it is a service) and any settings values the object requires (or mandatory values in the case of a data object). With the services, it often helps when they are interfaces rather than concrete types, too.

I said general. If there are one or two actions that need a particular service, and there are 15 others that do not, I might suggest that those one or two methods should either be part of another class, should take that dependency as a method parameter instead, or better yet, they may just need to return a calculated result, and let the caller use that result to make its own call, removing the need for the dependency at all.

That last bit probably needs an example.

An Example

Suppose we have the following code:

public class WidgetService
{
    private readonly IReviewSubmitService _rss;
    public WidgetService(IReviewSubmitService rss)
    {
        _rss = rss;
    }

    //15 other methods that do not use _rss

    public void PostUsageReport()
    {
        DataPackage data;
        //Some code generating a DataPackage
        _rss.PostReport(data);
    }
}

A better solution would be to refactor to the following:

public class WidgetService
{
    public WidgetService()
    {
        //No more need to pass a ReviewSubmitService
    }

    //15 other methods that do not use _rss

    public DataPackage GenerateUsageReport()
    {
        DataPackage data;
        //Some code generating a DataPackage
        //no longer call _rss;
        return data;
    }
}

var dataReport = _widgetService.GenerateUsageReport();
_rss.PostReport(dataReport);

For those paying attention, this is also a more functional way of programming. More generally, we refactor code flow so instead of A=>B=>C=>D we have A=>B, A=>C, A=>D. The dependencies are much less coupled from either other.

The ServiceLocator Pattern

Myself (and many others) absolutely hate the ServiceLocator pattern. But it is often thrust upon us as we work on legacy applications or with mis-informed technical leads. But that is another article. In this instance, I will take the stance that you have to use ServiceLocator. Here is a typical (in my opinion WRONG) example of a constructor:

public class WidgetService
{
    private readonly IReviewSubmitService _rss;
    private readonly IRepositoryServiceFactory _rsf;
    private readonly IOptimisedMarkupGenerator _omg;

    public WidgetService()
    {
        _rss = ServiceLocator.Resolve<IReviewSubmitService>();
        _rsf = ServiceLocator.Resolve<IRepositoryServiceFactory>();
        _omg = ServiceLocator.Resolve<IOptimisedMarkupGenerator>();
    }
}

and now the good example:

public class WidgetService
{
    private readonly IReviewSubmitService _reviewSubmitService;
    private readonly IRepositoryServiceFactory _repositoryServiceFactory;
    private readonly IOptimisedMarkupGenerator _optimisedMarkupGenerator;

    public WidgetService()
        this(
            ServiceLocator.Resolve<IReviewSubmitService>(),
            ServiceLocator.Resolve<IRepositoryServiceFactory>(),
            ServiceLocator.Resolve<IOptimisedMarkupGenerator>()
        )
    {
    }

    public WidgetService(
        IReviewSubmitService reviewSubmitService,
        IRepositoryServiceFactory repositoryServiceFactory,
        IOptimisedMarkupGenerator optimisedMarkupGenerator
    )
    {
        _reviewSubmitService = reviewSubmitService;
        _repositoryServiceFactory = repositoryServiceFactory;
        _optimisedMarkupGenerator= optimisedMarkupGenerator
    }
}

(I know I need another post around that formatting too...)

So what has changed? The main difference is that you can inject different implementations of the interfaces, without needing to configure a static ServiceLocator. Big Win. This dual constructor setup shown in this example has the following benefits:

  • You can pass dependencies into the instance. For example, from Unit Tests.
  • The ServiceLocator can be still be used at runtime to ensure the right implementation can be resolved.
  • If you have a container that can inject instances, it will do so for you, picking the more complex constructor to use. This gives you a migration away from ServiceLocator going forward.
  • This almost makes ServiceLocator, since you can just ignore the default constructor, and the class looks more SOLID.

Validation

Something that is also helpful, to avoid the dreaded latent ObjectNullReferenceException, is to validate your inputs. Check they are within the valid range at the first opportunity:

(only showing the constructor for simplicity.)

    public WidgetService(
        IReviewSubmitService reviewSubmitService,
        IRepositoryServiceFactory repositoryServiceFactory,
        IOptimisedMarkupGenerator optimisedMarkupGenerator,
        int someIndex
    )
    {
        if(reviewSubmitService == null) throw new ArgumentNullException(nameof(reviewSubmitService));
        if(repositoryServiceFactory == null) throw new ArgumentNullException(nameof(repositoryServiceFactory));
        if(optimisedMarkupGenerator == null) throw new ArgumentNullException(nameof(optimisedMarkupGenerator));
        if(someIndex < 0) throw new OutOfRangeException($"'{nameof(someIndex)}' must be a positive number.");

        _reviewSubmitService = reviewSubmitService;
        _repositoryServiceFactory = repositoryServiceFactory;
        _optimisedMarkupGenerator= optimisedMarkupGenerator
    }

Validate your arguments at construction time, and you will always find your null references and out of range values as soon as they appear and not deep down during a complex method execution, or worse, down the road when invalid data is read back from a database.

Multiple Settings

    public WidgetService(
        int index,
        int length,
        string moduleName,
        decimal conversion
    )
    {
        _index = index;
        _length = length;
        _moduleName = moduleName;
        _conversion = conversion;
    }

We have several values being passed in. During development it is likely we will be adding/removing the types and numbers of variables passed in. Lets encompass these in a datatype so that it is more maintainable, since we can foresee the usefulness of this.

public class WidgetServiceSettings
{
        public int Index { get; set; }
        public int Length { get; set; }
        public string ModuleName { get; set; }
        public decimal Conversion { get; set; }
}
    public WidgetService(
        WidgetServiceSettings settings,
    )
    {
        _settings = settings;
    }

Now the constructor won't have to change, as we add and remove extra settings required by the service, and it's various related methods. Image the benifit this has when you have a mixture of Settings values and Services passed into the constructor if we didn't do this?

Wrapup

A brief overview of my thoughts on Constructors. Hopefully these behaviours help make your code more composable, more testable, and more readable.

Did I miss something? Let me know.