© Vaskaran Sarcar 2020
V. SarcarDesign Patterns in C#https://doi.org/10.1007/978-1-4842-6062-3_5

5. Abstract Factory Pattern

Vaskaran Sarcar1 
(1)
Garia, Kolkata, West Bengal, India
 

This chapter covers the Abstract Factory pattern.

GoF Definition

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

Note

The Abstract Factory pattern will make more sense to you if you understand the Simple Factory pattern (Chapter 24) and the Factory Method pattern (Chapter 4). The Simple Factory pattern does not fall directly into the Gang of Four design patterns, so the discussion of that pattern appears in Part II of the book. I suggest that you read Chapters 4 and 24 before jumping into this one.

Concept

An abstract factory is often referred to as a factory of factories . This pattern provides a way to encapsulate a group of individual factories that have a common theme. In this process, you do not instantiate a class directly; instead, you instantiate a concrete factory and, after that, create products using the factory.

In our upcoming example, a factory instance (animalFactory) is instantiated. By using this factory instance, I create dog and tiger instances (dogs and tigers are the final products), which is why you see the following segment inside the client code.
// Making a wild dog and wild tiger through WildAnimalFactory
IAnimalFactory animalFactory = FactoryProvider.GetAnimalFactory("wild");
IDog dog = animalFactory.GetDog();
ITiger tiger = animalFactory.GetTiger();
dog.AboutMe();
tiger.AboutMe();

This pattern suits best when products are similar, but the product families are different (for example, a domestic dog is quite different from a wild dog). This pattern helps you to interchange specific implementations without changing the code that uses them, even at runtime. However, it may result in unnecessary complexity and extra work. Even debugging becomes tough in some cases.

Real-World Example

Suppose that you are decorating your room with two different types of tables; one is made of wood and the other one of steel. For the wooden type, you need to visit a carpenter, and for the other type, you may need to go to a metal shop. All of these are table factories. So, based on demand, you decide what kind of factory you need.

Computer-World Example

ADO.NET implements similar concepts to establish a connection to a database.

Implementation

Wikipedia describes a typical structure of this pattern, which is similar to what is shown in Figure 5-1 (see https://en.wikipedia.org/wiki/Abstract_factory_pattern).
../images/463942_2_En_5_Chapter/463942_2_En_5_Fig1_HTML.jpg
Figure 5-1

Abstract Factory pattern

I follow a similar structure in this chapter’s implementation. In this example, there are two types of animals: pet animals and wild animals. Program.cs is the client who is looking for some animals (which are wild dogs, pet dogs, wild tigers, and pet tigers in this case). You explore the construction processes of both pet animals and wild animals in this implementation.

IAnimalFactory is an abstract factory. Two concrete factories called WildAnimalFactory and PetAnimalFactory inherits from this abstract factory. You can see that these concrete factories are responsible for creating the concrete products of dogs and tigers. As their names suggest, WildAnimalFactory creates wild animals (wild dogs and wild tigers), and PetAnimalFactory creates pet animals (pet dogs and pet tigers). The following summarizes the participants and their roles.
  • IAnimalFactory: Abstract factory

  • WildAnimalFactory : A concrete factory that implements IAnimalFactory; it creates wild dogs and wild tigers

  • PetAnimalFactory : A concrete factory that implements IAnimalFactory, but this factory creates pet dogs and pet tigers

  • ITiger and IDog: Abstract products

  • PetTiger, PetDog, WildTiger, and WildDog: The concrete products. PetTiger and WildTiger implement the ITiger interface. PetDog and WildDog implement the IDog interface. The IDog and ITiger interfaces have only one method, AboutMe(), which is used in both the Simple Factory pattern and Factory Method pattern.

  • A static class called FactoryProvider is used in the client code as follows:
    // Making a wild dog and wild tiger through
    // WildAnimalFactory
    IAnimalFactory animalFactory = FactoryProvider.GetAnimalFactory("wild");
    IDog dog = animalFactory.GetDog();
    ITiger tiger = animalFactory.GetTiger();
    dog.AboutMe();
    tiger.AboutMe();
  • From the bold line in the previous code segment, you can see that I’m not directly instantiating the factory instance; instead, I’m using the FactoryProvider static class to get the factory instance. (This class has a similar structure as to when you used the concrete factories in the Factory Method pattern.) FactoryProvider provides the appropriate factory based on the parameter passed inside GetAnimalFactory(...) method.

Class Diagram

Figure 5-2 shows the class diagram.
../images/463942_2_En_5_Chapter/463942_2_En_5_Fig2_HTML.jpg
Figure 5-2

Class diagram

Solution Explorer View

Figure 5-3 shows the high-level structure of the program.
../images/463942_2_En_5_Chapter/463942_2_En_5_Fig3_HTML.jpg
Figure 5-3

Solution Explorer view

Demonstration 1

Here’s the complete program.
using System;
namespace AbstractFactoryPattern
{
    // Abstract Factory
    public interface IAnimalFactory
    {
        IDog GetDog();
        ITiger GetTiger();
    }
    // Abstract Product-1
    public interface ITiger
    {
        void AboutMe();
    }
    // Abstract Product-2
    public interface IDog
    {
        void AboutMe();
    }
    // Concrete product-A1(WildTiger)
    class WildTiger : ITiger
    {
        public void AboutMe()
        {
            Console.WriteLine("Wild tiger says: I prefer hunting in jungles. Halum.");
        }
    }
    // Concrete product-B1(WildDog)
    class WildDog : IDog
    {
        public void AboutMe()
        {
            Console.WriteLine("Wild dog says: I prefer to roam freely in jungles. Bow-Wow.");
        }
    }
    // Concrete product-A2(PetTiger)
    class PetTiger : ITiger
    {
        public void AboutMe()
        {
            Console.WriteLine("Pet tiger says: Halum. I play in an animal circus.");
        }
    }
    // Concrete product-B2(PetDog)
    class PetDog : IDog
    {
        public void AboutMe()
        {
            Console.WriteLine("Pet dog says: Bow-Wow. I prefer to stay at home.");
        }
    }
    // Concrete Factory 1-Wild Animal Factory
    public class WildAnimalFactory : IAnimalFactory
    {
        public ITiger GetTiger()
        {
            return new WildTiger();
        }
        public IDog GetDog()
        {
            return new WildDog();
        }
    }
    // Concrete Factory 2-Pet Animal Factory
    public class PetAnimalFactory : IAnimalFactory
    {
        public IDog GetDog()
        {
            return new PetDog();
        }
        public ITiger GetTiger()
        {
            return new PetTiger();
        }
    }
    // Factory provider
    class FactoryProvider
    {
        public static IAnimalFactory GetAnimalFactory(string factoryType)
        {
            if (factoryType.Contains("wild"))
            {
                // Returning a WildAnimalFactory
                return new WildAnimalFactory();
            }
            else
           if (factoryType.Contains("pet"))
            {
                // Returning a PetAnimalFactory
                return new PetAnimalFactory();
            }
            else
            {
                throw new ArgumentException("You need to pass either wild or pet as argument.");
            }
        }
    }
    // Client
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Abstract Factory Pattern Demo.*** ");            // Making a wild dog and wild tiger through WildAnimalFactory
            IAnimalFactory animalFactory = FactoryProvider.GetAnimalFactory("wild");
            IDog dog = animalFactory.GetDog();
            ITiger tiger = animalFactory.GetTiger();
            dog.AboutMe();
            tiger.AboutMe();
            Console.WriteLine("******************");
            // Making a pet dog and pet tiger through PetAnimalFactory now.
            animalFactory = FactoryProvider.GetAnimalFactory("pet");
            dog = animalFactory.GetDog();
            tiger = animalFactory.GetTiger();
            dog.AboutMe();
            tiger.AboutMe();
            Console.ReadLine();
        }
    }
}

Output

Here’s the output.
***Abstract Factory Pattern Demo.***
Wild dog says: I prefer to roam freely in jungles. Bow-Wow.
Wild tiger says: I prefer hunting in jungles. Halum.
******************
Pet dog says: Bow-Wow.I prefer to stay at home.
Pet tiger says: Halum.I play in an animal circus.

Q&A Session

5.1 Both the IDog and ITiger interfaces contain methods that have the same names. For example, both interfaces contain the AboutMe() method . Is that mandatory?

No. You can use different names for your methods. Also, the number of methods can be different in these interfaces. However, in Chapter 24, I cover the Simple Factory pattern, and in Chapter 4, I cover the Factory Method pattern. In this chapter, I continued the examples, which is why I kept the same method.

5.2 What are the challenges of using an abstract factory like this?

Any change in the abstract factory forces you to propagate the modification to the concrete factories. Standard design philosophy suggests you to program to an interface, but not to an implementation. This is one of the key principles that developers should always keep in mind. In most scenarios, developers do not want to change their abstract factories.

Also, the overall architecture is complex, which is why debugging is very challenging in some cases.

5.3 How do you distinguish a Simple Factory pattern from a Factory Method pattern or an Abstract Factory pattern?

I discuss the differences between a Simple Factory pattern and a Factory Method pattern in the “Q&A Session” section in Chapter 4.

Let’s revise how the client code uses these factories, as shown in the following diagrams. Here’s a code snippet from the Simple Factory pattern.
IAnimal preferredType = null;
SimpleFactory simpleFactory = new SimpleFactory();
#region The code region that can vary based on users preference
/*
* Since this part may vary, we're moving the
* part to CreateAnimal() in SimpleFactory class.
*/
preferredType = simpleFactory.CreateAnimal();
#endregion
#region The codes that do not change frequently.
preferredType.AboutMe();
#endregion
Figure 5-4 shows the Simple Factory pattern.
../images/463942_2_En_5_Chapter/463942_2_En_5_Fig4_HTML.jpg
Figure 5-4

Simple Factory pattern

Here’s the code snippet from the Factory Method pattern.
// Creating a Tiger Factory
AnimalFactory tigerFactory = new TigerFactory();
// Creating a tiger using the Factory Method
IAnimal tiger = tigerFactory.CreateAnimal();
tiger.AboutMe();
// Creating a DogFactory
AnimalFactory dogFactory = new DogFactory();
// Creating a dog using the Factory Method
IAnimal dog = dogFactory.CreateAnimal();
dog.AboutMe();
Figure 5-5 shows the Factory Method pattern.
../images/463942_2_En_5_Chapter/463942_2_En_5_Fig5_HTML.jpg
Figure 5-5

Factory Method pattern

Here’s the code snippet from Abstract Factory pattern.
// Making a wild dog and wild tiger through WildAnimalFactory
IAnimalFactory animalFactory = FactoryProvider.GetAnimalFactory("wild");
IDog dog = animalFactory.GetDog();
ITiger tiger = animalFactory.GetTiger();
dog.AboutMe();
tiger.AboutMe();
Console.WriteLine("******************");
// Making a pet dog and pet tiger through PetAnimalFactory now.
animalFactory = FactoryProvider.GetAnimalFactory("pet");
dog = animalFactory.GetDog();
tiger = animalFactory.GetTiger();
dog.AboutMe();
tiger.AboutMe();
Figure 5-6 shows the Abstract Factory pattern.
../images/463942_2_En_5_Chapter/463942_2_En_5_Fig6_HTML.jpg
Figure 5-6

Abstract Factory pattern

In short, with the Simple Factory pattern, you can separate the code that varies from the rest of the code (basically, you decouple the client code). This approach helps you to manage the code more easily. Another key advantage of this approach is that the client is unaware of how the objects are created. So, it promotes both security and abstraction.

However, this approach can violate the open-closed principle. You can overcome this drawback using the Factory Method pattern, which allows subclasses to decide how the instantiation process is completed. Put simply, you delegate the object creation to the subclasses that implement the factory method to create objects.

The abstract factory is basically a factory of factories. It creates a family of related objects, but it does not depend on the concrete classes. In this pattern, you encapsulate a group of individual factories that have a common theme. In this process, you do not instantiate a class directly; instead, you get a concrete factory (I used a provider for that) and, after that, create products using the factory.

Lastly, I tried to keep the examples simple. A factory method promotes inheritance, and its subclasses need to implement the factory method to create objects. The Abstract Factory pattern can promote object composition by creating related objects using the methods that are exposed in a factory interface. In the end, all the factories promote loose coupling by reducing the dependencies on concrete classes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.156.35