Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Dependency Injection of an Abstract Factory

0.00/5 (No votes)
30 May 2014 1  

Introduction

Application models like The Onion Architecture by Jeffrey Palermo are based on Inversion of Control. Dependency injection is the usual way to realize it.

A classic example used to present the technique is to inject an infrastructure repository to a business class via the constructor:

public BusinessClassA 
{  
    private readonly IRepositoryA _repositoryA;

    public BusinessClassA(IRepositoryA repositoryA)    
    {          
        _repositoryA = repository;     
    }
      
    […]  
}   
Colourised in 3ms

However, in real-life projects, applying this way to proceed for each infrastructure service has some limits:

  • More infrastructure repository/proxy/service could be required by the business class and the constructor could explode: ctor(IRepositoryA repositoryA, IRepositoryB repositoryB, IProxyC proxyC, ….);

  • The lifecycle of the instance of the infrastructure service should be independently managed from the one of business class. This does not occur in the example, since the repository is a private filed and initialized once;

This article proposes to inject a factory instead of infrastructure services directly in order to solve both these problems.

Background

The Abstract Factory pattern

“Provide an interface for creating families of related or dependent objects without specifying their concrete classes.”

Inversion of Control

"Don't call us, we'll call you (Hollywood's principle)".

Usually, if a class of type A uses a class of type B (flow of functional dependency), B needs to be compiled before A.

But when we introduce an interface for class B, put it into the component of A and delegate the creation of the concrete implementation of B to an external Factory Container, A could be compiled before B.

In this way the flow of source code dependency is opposed to the flow of functional dependency (this is the meaning of the term inversion).

Inversion of Control can be achieved in two way:

  • Dependency Injection (constructor, parameter, setter or interface injection): using a builder object to initialize objects and providing the required dependencies in order to "inject" a dependency from outside the class.

  • Service Locator: introducing a locator object that is used to "resolve" a dependency within a class.

The proposed solution in an example

Let consider a simple application modeled with the onion-architecture. The application manages a school. Consider a scenario in which we want to perform a bulk notification to all school member about Christmas Holiday.

The model of the demo application is composed by the actors of a school: students and trainers.

namespace Tiskali.Core.Model
{
    public abstract class Person
    {
        public virtual Guid ID { get; set; }
        public virtual string PIN { get; set; }
        public virtual string Name { get; set; }
        public virtual string Surname { get; set; }
        public virtual string Email { get; set; }
    }
} 
Colourised in 7ms

namespace Tiskali.Core.Model
{
    public partial class Student : Person
    {
    }
} 
Colourised in 1ms

namespace Tiskali.Core.Model
{
    public partial class Trainer : Person
    {
    }
} 
Colourised in 3ms

The scenario of the Use Case Module is incapsulatad in an Application Service as a transaction script. The service needs two infrastructure services: one repository (in order to retrieve all people registered in the system that are the target of the notification) and one notification engine.

Obtaining the notification engine on demand is a key point because its lifecycle become manageable. We can assume that the notification engine wrap System.Net.Mail.SmtpClient that is disposable. Obtaining the service on demand let us to dispose it with the using construct.

namespace Tiskali.Core.Services
{
    /// <summary>
    /// Use Case Module: Notification of events
    /// </summary>
    public class NotificationService : INotificationService
    {
        #region Private fields
        private readonly IServiceFactory _factory;
        #endregion

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="factory">Abstract factory</param>
        public NotificationService(IServiceFactory factory)
        {
            _factory = factory; // IoC by Dependency Injection
        }

        /// <summary>
        /// Notify a closure
        /// </summary>
        /// <param name="info">Infos about closure</param>
        public void NotifySchoolClosure(ClosureInfo info)
        {
            var personRepository = _factory.CreatePersonRepository();
                                                // IoC by Service Locator
            
            var people = personRepository.GetAllActivePeople();
            
            NotifySchoolClosure(info, people);
        }

        private void NotifySchoolClosure(ClosureInfo info, IEnumerable<Person> people)
        {
            using (var notificator = _factory.CreateEmailNotificator())
            {
                foreach (var person in people)
                {
                    notificator.SendNotification(person.Email, FormatMessage(person, info));
                }
            }
        }

        private string FormatMessage(Person person, ClosureInfo info)
        {
            var messageBuilder = new StringBuilder();
           
            messageBuilder.Append(string.Format("Hi {0}. ", person.Name));
            messageBuilder.Append(info.Reason);
            messageBuilder.Append(string.Format(" The school will be close from {0} to {1}", 
                                                    info.StartDate.Date, info.EndDate));
            messageBuilder.Append("Best regards.");

            return messageBuilder.ToString();
        }
    }
}  
Colourised in 62ms

Here below the interfaces of infrastructure services.

namespace Tiskali.Core.Repository
{
    /// <summary>
    /// Repository of people
    /// </summary>
    public interface IPersonRepository
    {
        /// <summary>
        /// Gets all currently active people
        /// </summary>
        /// <returns>All currently people</returns>
        IEnumerable<Person> GetAllActivePeople();
    }
} 


namespace Tiskali.Core.Notificators
{
    /// <summary>
    /// Notificator service
    /// </summary>
    public interface IEmailNotificator : IDisposable
    {
        /// <summary>
        /// Sends a notification (message) to a recipient (toAddress)
        /// </summary>
        /// <param name="toAddress">Recipient address</param>
        /// <param name="message">Contents of the notification</param>
        void SendNotification(string toAddress, string message);
    }
} 
Colourised in 22ms

The concrete implementation of both of them is empty in this simple demo

namespace Tiskali.Infrastructure.Repository
{
    /// <summary>
    /// Repository of people
    /// </summary>
    public class PersonRepository : IPersonRepository
    {
        private static ICollection<Person> _people = new List<Person>() {
                            new Student { ID = Guid.NewGuid(), Name = "Simona", Surname = "Bianchi", Email = "simona.bianchi@tiskali.it", PIN = "00001" },
                            new Student { ID = Guid.NewGuid(), Name = "Marco", Surname = "Rossi", Email = "marco.rossi@tiskali.it", PIN = "01001" },
                            new Student { ID = Guid.NewGuid(), Name = "Paolo", Surname = "Verdi", Email = "paolo.verdi@tiskali.it", PIN = "01002" },
                        };

        /// <summary>
        /// Gets all currently active people
        /// </summary>
        /// <returns>All currently people</returns>
        public IEnumerable<Person> GetAllActivePeople()
        {
            return _people.AsEnumerable();
        }
    }
}
namespace Tiskali.Infrastructure.Notificators
{
    /// <summary>
    /// Represents a concrete implementor of a email notificator
    /// </summary>
    public class EmailNotificator : IEmailNotificator
    {
        /// <summary>
        /// Sends a notification (message) to a recipient (toAddress)
        /// </summary>
        /// <param name="toAddress">Recipient address</param>
        /// <param name="message">Contents of the notification</param>
        public void SendNotification(string toAddress, string message)
        {
            // Here send the email with System.Net.Mail.SmtpClient
        }

        public void Dispose()
        {
            // Here dispose System.Net.Mail.SmtpClient
        }
    }
} 
Colourised in 40ms

The abstract factory is decomposed in more interfaces in order to take full advantage of Interface Segregation Principle.

namespace Tiskali.Core.Factories
{
    /// <summary>
    /// Abstract Factory for notification engine
    /// </summary>
    public interface IEmailNotificatorFactory
    {
        IEmailNotificator CreateEmailNotificator();
    }
} 

namespace Tiskali.Core.Factories
{
    /// <summary>
    /// Abstract Factory for repositories
    /// </summary>
    public interface IRepositoryFactory
    {
        IPersonRepository CreatePersonRepository();
    }
} 
namespace Tiskali.Core.Factories
{
    /// <summary>
    /// Abstract factory for all infrstructure services 
    ///     that are required by the core project
    /// </summary>
    public interface IServiceFactory : IRepositoryFactory, IEmailNotificatorFactory
    {

    }
} 
Colourised in 11ms

The concrete factory is defined in a separated project.

namespace Tiskali.Factories.Concrete
{
    public class ServiceFactory : IServiceFactory
    {
        public virtual IPersonRepository CreatePersonRepository()
        {
            return new PersonRepository();
        }

        public virtual IEmailNotificator CreateEmailNotificator()
        {
            return new EmailNotificator();
        }
    }
} 
Colourised in 5ms

The application is run by a simple console application.

namespace Tiskali.ConsoleApplication
{
    class Program
    {
        public Program()
        {
            RootContainer.RegisterAll(); // Forces IoC conteiner to register dependencies
        }

        static void Main(string[] args)
        {
            NotifyChristmasVacation(); //Executes a scenario of the Use Case Module
            
            Console.WriteLine("Press any key to terminate the program...");
            Console.ReadKey();
        }

        static void NotifyChristmasVacation()
        {
            INotificationService notificationService = RootContainer.Resolve<INotificationService>();
            var closureInfo = new ClosureInfo
            {
                StartDate = new DateTime(DateTime.Now.Year, 12, 24),
                EndDate = new DateTime(DateTime.Now.Year, 12, 26),
                Reason = "Christmas vacation."
            };
            notificationService.NotifySchoolClosure(closureInfo);
        }
    }
} 
Colourised in 23ms

The composition root takes place in RootContainer class.

namespace Tiskali.ConsoleApplication
{
    static class RootContainer
    {
        #region Private field
        private readonly static UnityContainer _container;
        #endregion

        /// <summary>
        /// Static ctor
        /// </summary>
        static RootContainer()
        {
            _container = new UnityContainer();
            RegisterAll();
        }

        internal static void RegisterAll()
        {
            _container.RegisterType<IEmailNotificatorFactory, ServiceFactory>();
            _container.RegisterType<IRepositoryFactory, ServiceFactory>();
            _container.RegisterType<IServiceFactory, ServiceFactory>();
            _container.RegisterType<INotificationService, NotificationService>();
        }

        internal static IofS Resolve<IofS>()
        {
            return _container.Resolve<IofS>();
        }
    }
} 
Colourised in 19ms

Using the code

The code is delivered a single solution (VS2013) containing four projects:

  • Tiskali.Core: It contains the business logic as transaction script, the anemic model; the definition of the abstract factory and the interfaces of the infrastructre services;

  • Tiskali.Infrastructure: It contains the concrete implementation of proxies, repositories, or any technology dependent class;

  • Tiskali.Factories: It contains the concrete implementations of the abstract factory;

  • Tiskali.ConsoleApplication: It runs the application;

History

2014-05-26: First Version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here