.NET tanker & tips

.Net, jQuery og andre nørdede emner

You are not Mozart - get help with composition

januar 07
by steffen 7. januar 2019 15:21

Mozart In one of my previous blog posts I wrote about dependency injection and why this should be a core principle when designing software architecture. The benefits from injecting dependencies into the classes who need them are many. One of the most important benefits is that your code will be loosely coupled, which makes the code flexible and make it much easier to reuse the code in multiple scenarios. A great benefit from loosely coupled code is also, that the code will be much easier to unit test. This allows us to assert that the code behaves as in the way that it was intended.

The most common way of implementing DI is by using constructor injection. This means that each class will take all their dependencies as an argument in the constructor. This pattern ensures that it is not possible to create an instance of a class, if you do not provide all the required dependencies. You can also inject dependencies by exposing the class dependencies through properties, allowing the client to initialize the dependencies. Personally I am not a fan of this approach, since this put the responsibility of initializing the correct dependencies on the client, which may or may not have the knowledge on the inner workings of the class. This can result in runtime exceptions, if a method is called, which depend on a uninitialized dependency.

When using DI the classes needed for a software system to work, must be build as close to the entry point of the system as possible. In a console application this would be in the Main method and in a MVC application it should be before the controller is executed. This is easily accomplished by using a DI framework (e.g. StructureMap). When using a DI framework, which I highly recommend, it will be responsible for creating the entire object graph, required for the system to function. The DI framework will analyze the dependencies of you code and based on convention and/or configuration it will spin up the required objects and inject them into the program.

A great example of a mature and well functioning DI framework is StructureMap (soon to be called Lamar). One of the cool features in StructureMap is that it has some default conventions, that allows you to get up and running with fewer lines of code. The main convention is that, unless specified in the composition root (the configuration method of StructureMap), for an interface called IFoo it will look for a concrete class called Foo. As such StructureMap rely on the normal naming convention where the name of an interface always start with an "I". This convention means that StructureMap will automatically map all interfaces to the respective concrete implementations, without you writing a single line of code.

Obviously most scenario will not consist exclusively of interfaces and classes with this naming relation. For all the other interface/class combinations, you must define which concrete classes must be used for which interfaces. An example could be that you have a ICar interface and you want StructureMap to inject an instance of the Jeep class (which implements the ICar interface). This is easily done with a short line of code in the configuration of StructureMap.

public class DefaultRegistry : Registry {

public DefaultRegistry() {

            Scan(

                scan => {

                    scan.TheCallingAssembly();

                    scan.WithDefaultConventions();

                });

            For<ICar>().Use<Jeep>();

        }

    }

The DefaultRegistry class and the code in the constructor, is responsible for creating the object graph. This is called the "composition root" and in a more complex application this class will be significantly bigger as the entire object graph of the application is defined here. One of the strong points by having all the configuration centralized in the composition root, is that you always know exactly where the object tree is constructed.

When using StructureMap to control the composition root, an added benefit is that it integrations seamlessly into most application types. When installing StructureMap from NuGet, you can almost certainly find a flavor of StructureMap that suit you application type. This means that as soon as StructureMap has been installed, it will be "plugged into" your application pipeline, ensuring that the composition root is called whenever the application needs an object graph.

Tags:

Dependency Injection | General

blog comments powered by Disqus