What is Abstract Factory Pattern? Real Example on C#

The Abstract Factory pattern provides an interface for creating families of related objects with specific interfaces without specifying specific object data types.

When to use an Abstract Factory?

  • When the system should not depend on how new objects are created and laid out
  • When the created objects are to be used together and are related

Abstract Factory – Regular Example Shown in Books

abstract class AbstractFactory
{
    public abstract AbstractProductA CreateProductA();
    public abstract AbstractProductB CreateProductB();
}
class ConcreteFactory1: AbstractFactory
{
    public override AbstractProductA CreateProductA()
    {
        return new ProductA1();
    }
         
    public override AbstractProductB CreateProductB()   
    {
        return new ProductB1(); 
    }
}
class ConcreteFactory2: AbstractFactory
{
    public override AbstractProductA CreateProductA()
    {
        return new ProductA2();
    }
         
    public override AbstractProductB CreateProductB()
    {
        return new ProductB2();
    }
}
 
abstract class AbstractProductA
{}
             
abstract class AbstractProductB     
{}
                 
class ProductA1: AbstractProductA   
{}
     
class ProductB1: AbstractProductB   
{}
 
class ProductA2: AbstractProductA   
{}
                 
class ProductB2: AbstractProductB       
{}      
             
class Client
{
    private AbstractProductA abstractProductA;
    private AbstractProductB abstractProductB;
 
    public Client(AbstractFactory factory)
    {
        abstractProductB = factory.CreateProductB();
        abstractProductA = factory.CreateProductA();
    }
    public void Run()
    { }
}

 

The pattern defines the following participants:

  1. The abstract classes AbstractProductA and AbstractProductB define an interface for classes whose objects will be created in the program.
  2. The concrete classes ProductA1 / ProductA2 and ProductB1 / ProductB2 represent the concrete implementation of the abstract classes
  3. The abstract factory class AbstractFactory defines methods for creating objects. It is important to notice that the methods return abstract products, not their concrete implementations.
  4. The concrete factory classes ConcreteFactory1 and ConcreteFactory2 implement the abstract methods of the base class and directly define which concrete products to use.
  5. The Client class uses a factory class to create objects. At the same time, it uses exclusively the abstract factory class AbstractFactory and the abstract product classes AbstractProductA and AbstractProductB and does not depend on their concrete implementations in any way.

Create an Email Sender using Abstract Fabric Pattern

This is a regular example shown by most books. At best, they also give an example with animals or geometric shapes. Such examples are good, but they do not show the actual application of this pattern. I wrote an example that is a bit close to the real situation.
Let’s say we have a freelance platform website. It has two main participants: the customer and the freelancer. Using the Abstract Factory we can implement the creation of confirmation and salutation emails. Thus, both users should receive two types of emails:

  • Profile Confirmation Email
  • Profile Salutation Email

Define Abstract and Concrete Classes

namespace ConsoleApp1
{
    abstract class ProfileEmailAbstract
    { 
        public string Subject { get; set; }
        public string Body { get; set; }
    }
    class FreelancerConfirmationEmail: ProfileEmailAbstract
    {
    }
    class FreelancerSalutationEmail: ProfileEmailAbstract
    {
    }
    class CustomerConfirmationEmail: ProfileEmailAbstract
    {
    }
    class CustomerSalutationEmail: ProfileEmailAbstract
    {
    }
    abstract class AbstractEmailFactory
    {
        public abstract ProfileEmailAbstract CreateConfirmationEmail();
        public abstract ProfileEmailAbstract CreateSalutationEmail();
    }
    class CustomerEmailFactory: AbstractEmailFactory
    {
        public override ProfileEmailAbstract CreateConfirmationEmail()
        {
            return new CustomerConfirmationEmail()
            {
                Body = "Dear Customer, please confirm your registration.",
                Subject = "Customer confirmation required"
            };
        }
        public override ProfileEmailAbstract CreateSalutationEmail()
        {
            return new CustomerSalutationEmail()
            {
                Body = "Dear Customer, thank you for confirmation. Welcome to our freelance website.",
                Subject = "Sucessfull registration"
            };
        }
    }
    class FreelancerEmailFactory: AbstractEmailFactory
    {
        public override ProfileEmailAbstract CreateConfirmationEmail()
        {
            return new FreelancerConfirmationEmail()
            {
                Body = "Dear Freelancer, please confirm your registration.",
                Subject = "Freelancer confirmation required"
            };
        }
        public override ProfileEmailAbstract CreateSalutationEmail()
        {
            return new FreelancerSalutationEmail()
            {
                Body = "Dear Freelancer, thank you for confirmation. Welcome to our freelance website.",
                Subject = "Sucessfull registration"
            };
        }
    }
}

This is very similar to what you saw in the example above.

Define Email Client

namespace ConsoleApp1
{
    internal class EmailClient
    {
        private ProfileEmailAbstract confirmationEmail;
        private ProfileEmailAbstract salutationEmail;
        public EmailClient(AbstractEmailFactory factory)
        {
            confirmationEmail = factory.CreateConfirmationEmail();
            salutationEmail = factory.CreateSalutationEmail();
        }
        public void SendConfirmationEmail()
        {
            Console.WriteLine("Confirmation Email was sent.");
            Console.WriteLine($"Subject: {confirmationEmail.Subject}");
            Console.WriteLine($"Body: {confirmationEmail.Body}");
            Console.WriteLine(Environment.NewLine);
        }
        public void SendSalutationEmail()
        {
            Console.WriteLine("Salutation Email was sent.");
            Console.WriteLine($"Subject: ", salutationEmail.Subject);
            Console.WriteLine($"Body: {salutationEmail.Body}");
            Console.WriteLine(Environment.NewLine);
        }
    }
}

 

Call aka Send Emails

using ConsoleApp1;
Console.WriteLine("Press Enter to Send Emails for CUSTOMER.");
Console.ReadKey();
var customerConfirmationEmailFactory = new CustomerEmailFactory();
var customerClient = new EmailClient(customerConfirmationEmailFactory);
customerClient.SendConfirmationEmail();
customerClient.SendSalutationEmail();
Console.WriteLine("Press Enter to Send Emails for FREELANCER.");
Console.ReadKey();
var freelancerConfirmationEmailFactory = new FreelancerEmailFactory();
var freelancerClient = new EmailClient(freelancerConfirmationEmailFactory);
freelancerClient.SendConfirmationEmail();
freelancerClient.SendSalutationEmail();
Console.ReadKey();

This is a front part of our program where we initiate email sending. Here is an example of program execution:
c# abstract fabric email sender example

Abstract Fabric Pros and Cons

Pros
  • The factory hides the implementation details of concrete classes and the creation process from the client instances of these classes.
  • An instance of a concrete factory class is created in the application in one place and only once, which makes it easier to replace factories in the future.
  • Allows you to easily control the interaction between product objects that designed to be used together and are part of the same family.

Cons
  • There is a slight inconvenience of adding a new kind of product.

Well-Known Examples from Microsoft

Microsoft.Build.Tasks.CodeTaskFactory
http://msdn.microsoft.com/en-us/library/microsoft.build.tasks.codetaskfactory.aspx
Microsoft.Build.Tasks.XamlTaskFactory
http://msdn.microsoft.com/en-us/library/microsoft.build.tasks.xamltaskfactory.aspx
Microsoft.IE.SecureFactory
http://msdn.microsoft.com/en-us/library/microsoft.ie.securefactory(v=vs.90).aspx
System.Activities.Presentation.Model.ModelFactory
http://msdn.microsoft.com/en-us/library/system.activities.presentation.model.modelfactory.aspx
System.Data.Common.DbProviderFactory
http://msdn.microsoft.com/en-us/library/system.data.common.dbproviderfactory.aspx
System.Data.EntityClient.EntityProviderFactory
http://msdn.microsoft.com/en-us/library/system.data.entityclient.entityproviderfactory.aspx
System.Data.Odbc.OdbcFactory
http://msdn.microsoft.com/en-us/library/system.data.odbc.odbcfactory.aspx
System.Data.OleDb.OleDbFactory
http://msdn.microsoft.com/en-us/library/system.data.oledb.oledbfactory.aspx
System.Data.OracleClient.OracleClientFactory
http://msdn.microsoft.com/en-us/library/system.data.oracleclient.oracleclientfactory.aspx
System.Data.Services.DataServiceHostFactory
http://msdn.microsoft.com/en-us/library/system.data.services.dataservicehostfactory.aspx
System.Data.SqlClient.SqlClientFactory
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlclientfactory.aspx
System.ServiceModel.ChannelFactory
http://msdn.microsoft.com/en-us/library/system.servicemodel.channelfactory.aspx
System.Threading.Tasks.TaskFactory
http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory(v=vs.110).aspx
System.Web.Compilation.ResourceProviderFactory
http://msdn.microsoft.com/en-us/library/system.web.compilation.resourceproviderfactory.aspx
System.Web.Hosting.AppDomainFactory
http://msdn.microsoft.com/en-us/library/system.web.hosting.appdomainfactory(v=vs.90).aspx
System.Xml.Serialization.XmlSerializerFactory
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializerfactory(v=vs.90).aspx

Leave a Comment