I currently work for a small company that provides software services to the financial and banking industry. We provide a a rather large SDK that allows for rapid development of custom solutions, however, it became evident that in order to freely iterate over specific parts of our core product that unnecessary code dependency within our product line would have to be removed.
This pervasive code inter dependence forced us to perform vast rebuilds to replace or update even mundane portions of source code. Classes became difficult to test in isolation direct references to dependencies existed everywhere, thus the final implementation of the dependencies had to be available at compile time. Over the last three plus years these issues have been overcome by implementing a variety Inversion of Control techniques.
The Inversion of Control (IoC) pattern can be implemented in several ways, personally I have pretty much settled on using and supporting the Dependency Injection (DI) pattern and/or using an IoC Container framework.
Dependency Injection
Imagine the following class, unfortunately it is designed such that the dependent UploadImage class is directly tied to implementation of PhotoEditor. Now any changes required to UploadImage are directly tied to the compilation of the main class.
public class PhotoEditor { private UploadImage _uploader; public PhotoEditor() { this._uploader = new UploadImage(); } public Upload() { this._uploader.Send(); } }
The following shows PhotoEditor again but this time the upload class is implemented with an interface. This decoupling of the actual implementation means that we can complete wholesale changes to UploadImage, and because the PhotoEditor class uses dependency injection, its dependencies can be replaced with mock implementations when testing.
public class PhotoEditor { private IUploadImage _uploader; public PhotoEditor(IUploadImage uploader) { this._uploader = uploader; } public Upload() { this._uploader.Send(); } }
The dependency injection pattern has a few liabilities for example there are more solution elements to manage. You have to ensure that, before initializing objects, that the dependency injection framework can resolve the dependencies required. Finally the code can become more difficult to follow.
IoC Container
The focus of an IoC/DI framework is an object called a container which will obtain knowledge about components needed by your application, it will attempt to figure out which component you might use. In practice the IoC container will allow us to register components into the container and then you ask the container to give you an instance of a component, this means you no longer need to instantiate classes using the traditional constructors mechanism. Our pattern is as follows:
- Create a ContainerBuilder.
- Register components.
- Build the container and store it for later use.
- Create a lifetime scope from the container.
- Use the lifetime scope to resolve instances of the components.
Here is an example of this premise using AutoFac:
using System; using Autofac; namespace DemoApp { public class Program { private static IContainer Container { get; set; } static void Main(string[] args) { var builder = new ContainerBuilder(); builder.RegisterType().As (); builder.RegisterType ().As (); Container = builder.Build(); WriteDate(); } public static void WriteDate() { // Create the scope, resolve your IDateWriter, // use it, then dispose of the scope. using (var scope = Container.BeginLifetimeScope()) { var writer = scope.Resolve (); writer.WriteDate(); } } } }
Also check out the Castle Windsor IoC container for an alternate solution approach.
Comments are closed.