Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Friday, October 28, 2016

Abstract Factory Pattern

What is Abstract Factory Pattern?

Abstract factory pattern is a creational pattern and provide an interface for creating families of related or dependent objects without specifying their concrete classes.

Abstract Factory patterns work around a super-factory which creates other factories. This factory is also called as factory of factories.

The Abstract Factory defines a Factory Method per product.

When to use it?

  1. Create a set of related objects, or dependent objects which must be used together.

  2. System should be configured to work with multiple families of products.

  3. The creation of objects should be independent from the utilizing system.

  4. Concrete classes should be decoupled from clients.

The classes and objects participating in this pattern are:

  • AbstractFactory  (IWaltonFactory)
    • This is an interface which is used to create abstract product.
  • ConcreteFactory  (BangladeshFactory, BhutanFactory)
    • This is a class which implements the AbstractFactory interface to create concrete products.
  • AbstractProduct  (IMobile, IMotorcycle)
    • This is an interface which declares a type of product.
  • Product  (PrimoNx, PrimoZx, Fusion, Xplore)
    • This is a class which implements the AbstractProduct interface to create product.
  • Client  (WaltonClient)
    • This is a class which use AbstractFactory and AbstractProduct interfaces to create a family of related objects.

Real world example:

Let's Walton company has a number of factories throughout the world.

They are building number of products like mobile, motorcycle, television, freez etc.

Client can create any product from any factory. Here I'm showing how we can create a product using Abstract Factory pattern.
  public enum MobileModelType
    {
        PrimoNx,
        PrimoZx
    }

    public enum MotorcycleModelType
    {
        Fusion,
        Xplore
    }

    public interface IWaltonFactory
    {
        IMobile GetMobile(MobileModelType modelType);

        IMotorcycle GetMotorcycle(MotorcycleModelType modelType);
    }
    
    public class BangladeshFactory : IWaltonFactory
    {
        public IMobile GetMobile(MobileModelType modelType)
        {
            IMobile mobile;
            switch (modelType)
            {
                case MobileModelType.PrimoNx:
                    mobile = new PrimoNx();
                    break;
                case MobileModelType.PrimoZx:
                    mobile = new PrimoZx();
                    break;
                default:
                    throw new ApplicationException("Unknown model type");
            }
            mobile.FactoryName = "Bangladesh";
            return mobile;
        }

        public IMotorcycle GetMotorcycle(MotorcycleModelType modelType)
        {
            IMotorcycle motorcycle;
            switch (modelType)
            {
                case MotorcycleModelType.Fusion:
                    motorcycle = new Fusion();
                    break;
                case MotorcycleModelType.Xplore:
                    motorcycle = new Xplore();
                    break;
                default:
                    throw new ApplicationException("Unknown model type");
            }
            motorcycle.FactoryName = "Bangladesh";
            return motorcycle;
        }
    }
    
    public class BhutanFactory : IWaltonFactory
    {
        public IMobile GetMobile(MobileModelType modelType)
        {
            IMobile mobile;
            switch (modelType)
            {
                case MobileModelType.PrimoNx:
                    mobile = new PrimoNx();
                    break;
                case MobileModelType.PrimoZx:
                    mobile = new PrimoZx();
                    break;
                default:
                    throw new ApplicationException("Unknown model type");
            }
            mobile.FactoryName = "Bhutan";
            return mobile;
        }

        public IMotorcycle GetMotorcycle(MotorcycleModelType modelType)
        {
            IMotorcycle motorcycle;
            switch (modelType)
            {
                case MotorcycleModelType.Fusion:
                    motorcycle = new Fusion();
                    break;
                case MotorcycleModelType.Xplore:
                    motorcycle = new Xplore();
                    break;
                default:
                    throw new ApplicationException("Unknown model type");
            }
            motorcycle.FactoryName = "Bhutan";
            return motorcycle;
        }
    }

   
    public interface IMobile
    {
        string FactoryName { get; set; }
        void Create();
    }

   public interface IMotorcycle
    {
        string FactoryName { get; set; }
        void Create();
    }

    public class PrimoNx : IMobile
    {
        public string FactoryName { get; set; }

        public void Create()
        {
            Console.WriteLine($"Primo Nx model mobile has been created from {this.FactoryName}.");
        }
    }

  public class PrimoZx : IMobile
    {
        public string FactoryName { get; set; }

        public void Create()
        {
            Console.WriteLine($"Primo Zx model mobile has been created from {this.FactoryName}.");
        }
    }

   public class Fusion : IMotorcycle
    {
        public string FactoryName { get; set; }

        public void Create()
        {
            Console.WriteLine($"Fusion model motorcycle  has been created from {this.FactoryName}.");
        }
    }

   
    public class Xplore : IMotorcycle
    {
        public string FactoryName { get; set; }

        public void Create()
        {
            Console.WriteLine($"Xplore model motorcycle  has been created from {this.FactoryName}.");
        }
    }

    public class WaltonClient
    {
        private readonly IWaltonFactory factory;

        public WaltonClient(IWaltonFactory factory)
        {
            this.factory = factory;
        }

        public void CreateMobile(MobileModelType modelType)
        {
            var mobile = this.factory.GetMobile(modelType);
            mobile.Create();
        }

        public void CreateMotorcycle(MotorcycleModelType modelType)
        {
            var motorcycle = this.factory.GetMotorcycle(modelType);
            motorcycle.Create();
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            IWaltonFactory waltonBangladeshFactory = new BangladeshFactory();
            WaltonClient waltonClient = new WaltonClient(waltonBangladeshFactory);
            waltonClient.CreateMobile(MobileModelType.PrimoNx);
            waltonClient.CreateMotorcycle(MotorcycleModelType.Fusion);

            IWaltonFactory waltonBhutanFactory = new BhutanFactory();
             waltonClient = new WaltonClient(waltonBhutanFactory);
            waltonClient.CreateMobile(MobileModelType.PrimoZx);
            waltonClient.CreateMotorcycle(MotorcycleModelType.Xplore);

            Console.ReadKey();

        }
    }
  

Here is the output result:

*********************** Bangladesh Factory ********************************
Primo Nx model mobile has been created from Bangladesh.
Fusion model motorcycle  has been created from Bangladesh.



*********************** Bhutan Factory ********************************
Primo Zx model mobile has been created from Bhutan.
Xplore model motorcycle  has been created from Bhutan.


Note

  1. Internally, Abstract Factory use Factory design pattern for creating objects. But it can also use Builder design pattern and prototype design pattern for creating objects. It completely depends upon your implementation for creating objects.

  2. Abstract Factory can be used as an alternative to Facade to hide platform-specific classes.

  3. When Abstract Factory, Builder, and Prototype define a factory for creating the objects, we should consider the following points :

    1. Abstract Factory use the factory for creating objects of several classes.

    2. Builder use the factory for creating a complex object by using simple objects and a step by step approach.

    3. Prototype use the factory for building a object by copying an existing object.

Sunday, September 25, 2016

Refactoring if else-if and switch statement code block

I will show you how we can refactor an if else-if or switch case statement code block in more batter way.

Though I'm using C# language here but you can use this approach in other programming languages too.

Before Refactoring

Let's we have a switch statement code block in our application which returns the Excel file name depending on ExportType enum.
         public string GetReportFileName(ExportType exportType)
          {
              string fileName;
              switch (exportType)
              {
                  case ExportType.MemberDataVersioning:
                      fileName = "MemberList.xls";
                      break;
                  case ExportType.MemberProfileInformation:
                      fileName = "MemberProfile.xls";
                      break;
                  case ExportType.MemberOtherDataVersioningDetails:
                      fileName = "MemberRecords.xls";
                      break;
                  case ExportType.ProviderDataVersioning:
                      fileName = "ProviderList.xls";
                      break;
                  case ExportType.ProviderProfileInformation:
                      fileName = "ProviderProfile.xls";
                      break;
                  case ExportType.ProviderOtherDataVersioningDetails:
                      fileName = "ProviderRecords.xls";
                      break;
                  case ExportType.ClaimsDataVersioning:
                      fileName = "ClaimList.xls";
                      break;
                  case ExportType.ClaimsDataChangeLog:
                      fileName = "ClaimDataChangeLog.xls";
                      break;
                  case ExportType.ElementWiseValidationReport:
                      fileName = "ElementDetails.xls";
                      break;
                  case ExportType.ReconciliationReport:
                      fileName = "RecociliationReport.xls";
                      break;

                  default:
                      fileName = exportType.ToString() + ".xls";
                      break;
              }

              return fileName;
          }
                    
When a new export type will come then we have to add another switch case statement and will continue this process for every new export type.

After Refactoring

There is nice refactoring way which we can apply to improve the code quality and simplicity.
We can use a dictionary instead of the switch statement. Let's we declare a dictionary of report file types in constructor:
        private readonly Dictionary<ExportType, string> reportFileNames;
    
        public ReportFileHelper()
          {
              this.reportFileNames = new Dictionary<ExportType, string>
                                    {
                                        { ExportType.MemberDataVersioning, "MemberList.xls" },
                                        { ExportType.MemberProfileInformation, "MemberProfile.xls" },
                                        { ExportType.MemberOtherDataVersioningDetails, "MemberRecords.xls" },
                                        { ExportType.ProviderDataVersioning, "ProviderList.xls" },
                                        { ExportType.ProviderProfileInformation, "ProviderProfile.xls" },
                                        { ExportType.ProviderOtherDataVersioningDetails, "ProviderRecords.xls" },
                                        { ExportType.ClaimsDataVersioning, "ClaimList.xls" },
                                        { ExportType.ClaimsDataChangeLog, "ClaimChangeLog.xls" },
                                        { ExportType.ElementWiseValidationReport, "ElementDetails.xls" },
                                        { ExportType.ReconciliationReport, "RecociliationReport.xls" }
                                    };
          }
                    
Now we can easily refactore the GetReportFileName method with this simple lines of code:
         public string GetReportFileName(ExportType exportType)
          {
            string fileName; 
            return reportFileNames.TryGetValue(exportType, out fileName) ? fileName : (exportType.ToString() + ".xls");
          }
                    

When a new report type comes, we will just add an item in reportFileNames dictionary.

Similarly, we can also use this refactoring approach in the if else-if statement.

Before Refactoring

Let's we have a function which return a customer object based on customer type.

Here is the code block:

    public ICustomer GetCustomer(CustomerType customerType)
        {
            ICustomer customer = null;
            if (customerType == CustomerType.Gold)
            {
                customer = new GoldCustomer();
            }
            else if (customerType == CustomerType.Platinum)
            {
                customer = new PlatinumCustomer();
            }
            else if (customerType == CustomerType.Silver)
            {
                customer = new SilverCustomer();
            }
            else if (customerType == CustomerType.Normal)
            {
                customer = new NormalCustomer();
            }

            return customer;
        }
                    

After Refactoring

We will declare a dictionary in the constructor. Here I have used Func of the customer.

Func is a very nice feature in c#. you can get details from here.

        private readonly Dictionary<CustomerType, Func<ICustomer>> customerTypes;

        public CustomerFactory()
        {
            this.customerTypes = new Dictionary<CustomerType, Func<ICustomer>>
                      {
                          { CustomerType.Gold, () => new GoldCustomer() },
                          { CustomerType.Platinum, () => new PlatinumCustomer() },
                          { CustomerType.Silver, () => new SilverCustomer() },
                          { CustomerType.Normal, () => new NormalCustomer() },
                      };
        }
                    

Now we can replace the GetCustomer method in this way.

    public ICustomer GetCustomer(CustomerType customerType)
        {
            Func<ICustomer> customer;
            return this.customerTypes.TryGetValue(customerType, out customer) ? customer() : null;
        }
                    
Any feedbacks are most welcomed. If you know any better approach, please share it here.