Click here to Skip to main content
12,754,745 members (36,549 online)
Click here to Skip to main content
Add your own
alternative version

Stats

17K views
8 downloads
20 bookmarked
Posted 29 May 2014

Dependency Injection of an Abstract Factory

, 30 May 2014 CPOL
Rate this:
Please Sign up or sign in to vote.

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;     
    }
      
    […]  
}   

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; }
    }
} 

namespace Tiskali.Core.Model
{
    public partial class Student : Person
    {
    }
} 

namespace Tiskali.Core.Model
{
    public partial class Trainer : Person
    {
    }
} 

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();
        }
    }
}  

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);
    }
} 

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
        }
    }
} 

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
    {

    }
} 

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();
        }
    }
} 

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);
        }
    }
} 

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>();
        }
    }
} 

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Roberto Mameli
Software Developer
Italy Italy
No Biography provided

You may also be interested in...

Pro

Comments and Discussions

 
QuestionDownload source Code Pin
Member 84855679-Sep-14 0:47
memberMember 84855679-Sep-14 0:47 
GeneralMy vote of 3 Pin
Kaushal Jain24-Jun-14 8:42
memberKaushal Jain24-Jun-14 8:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.170217.1 | Last Updated 30 May 2014
Article Copyright 2014 by Roberto Mameli
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid