Labels

Monday, November 15, 2010

Abstract Factory - Creational

1.1     Introduction

The Abstract Factory Method pattern is a creational pattern. Like other creational patterns, it deals with the problem of creating objects without specifying the actual class of objects that will be created.

The Abstract Factory pattern is very similar to the Factory Method pattern. One difference between the two is that with the Abstract Factory pattern, a class delegates the responsibility of object instantiation to another object via composition whereas the Factory Method pattern uses inheritance and relies on a subclass to handle the desired object instantiation. Actually, the delegated object frequently uses factory methods to perform  the instantiation!

1.2     Intent

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

1.3     Applicability

Use the Abstract Factory pattern in any of the following situations:

·         A system should be independent of how its products are created, composed, and represented
·         A class can't anticipate the class of objects it must create
·         A system must use just one of a set of families of products
·         A family of related product objects is designed to be used together, and you need to enforce this constraint

1.4     Motivation

Consider a composite class which must contain the same members but be created differently depending on the application. For example, you could have a document which would be printed differently depending on the printer being used, but would still have to contain the same information. The idea behind abstract factory is that you can pass a class (the factory) to the constructor which has a standard interface. This interface is then used by the constructor, but the methods it uses are implemented differently for each kind of factory.



In the above class diagram, you can see how the Client Class function, CreateProduct, uses the AbstractFactory classes' CreateProduct function to create an object of the AbstractProduct class. Because there is no direct connection between the Client and the ConcreteProduct class, the ConcreteProduct can be any derived class of AbstractProduct.

1.5     Structure


1.6     Participants


AbstractProduct
Declare an interface for the type of product object
ConcreteProduct
Defines a product object to be created by the corresponding concrete factory.

Implements the Product interface
AbstractFactory
Declares an interface for operations that create abstract product objects.
ConcreteFactory
Implements the operations to create concrete product objects
Client
Uses only interfaces declared by AbstractFactory and AbstractProduct classes

1.7     Collaborations

·         Normally a single instance of a ConcreteFactory class is created at runtime. (This is an example of the Singleton Pattern.) This concrete factory creates product objects having a particular implementation. To create different product objects, clients should use a different concrete factory.
·         AbstractFactory defers creation of product objects to its ConcreteFactory

1.8     Discussion

A software design pattern, the Abstract Factory Pattern provides a way to encapsulate a group of individual factories that have a common theme. In normal usage, the client software would create a concrete implementation of the abstract factory and then use the generic interfaces to create the concrete objects that are part of the theme. The client does not know (nor care) about which concrete objects it gets from each of these internal factories since it uses only the generic interfaces of their products. This pattern separates the details of implementation of a set of objects from its general usage.

An example of this would be an abstract factory class DocumentCreator that provides interfaces to create a number of products (eg. createLetter() and createResume()). The system would have any number of derived concrete versions of the DocumentCreator class like FancyDocumentCreator or ModernDocumentCreator, each with a different implementation of createLetter() and createResume() that would create a corresponding object like FancyLetter or ModernResume. Each of these products is derived from a simple abstract class like Letter or Resume of which the client is aware. The client code would get an appropriate instantiation of the DocumentCreator and call its factory methods. Each of the resulting objects would be created from the same DocumentCreator implementation and would share a common theme. (They would all be fancy or modern objects.) The client would need to know how to handle only the abstract Letter or Resume class, not the specific version that it got from the concrete factory.

In software development, a Factory is the location in the code at which objects are constructed. The intent in employing the pattern is to insulate the creation of objects from their usage. This allows for new derived types to be introduced with no change to the code that uses the base object.

Use of this pattern makes it possible to interchange concrete classes without changing the code that uses them, even at runtime. However, employment of this pattern, as with similar design patterns, incurs the risk of unnecessary complexity and extra work in the initial writing of code.

1.9     Pros

·         Isolation of concrete classes:

The Abstract Factory pattern helps you control the classes of objects that an application creates. Because a factory encapsulates the responsibility and the process of creating product objects, it isolates clients from implementation classes. Clients manipulate instances through their abstract interfaces. Product class names are isolated in the implementation of the concrete factory; they do not appear in client code.
·         Exchanging Product Families easily:

The class of a concrete factory appears only once in an application, that is where it's instantiated. This makes it easy to change the concrete factory an application uses. It can use different product configurations simply by changing the concrete factory. Because an abstract factory creates a complete family of products, the whole product family changes at once.
·          Promoting consistency among products:

When product objects in a family are designed to work together, it's important that an application use objects from only one family at a time. AbstractFactory makes this easy to enforce.

1.10  Consequences

·         Adding a new product means changing factory interface + all concrete factories.

Extending abstract factories to produce new kinds of Products isn't easy. That's because the AbstractFactory interface fixes the set of products that can be created. Supporting new kinds of products requires extending the factory interface, which involves changing the AbstractFactory class and all of its subclasses. But you can overcome this problem by using reflection in .Net Framework and defining CreateProduct method in the AbstractFactory class.

1.11  Example1

This structural code demonstrates the Abstract Factory pattern creating parallel hierarchies of objects. Object creation has been abstracted and there is no need for hard-coded class names in the client code.




using System;

namespace Xpanxion.Structural.AbstractFactory
{
  // MainApp test application

  class MainApp
  {
    public static void Main()
    {
      // Abstract factory #1
      AbstractFactory factory1 = new ConcreteFactory1();
      Client c1 = new Client(factory1);
      c1.Run();

      // Abstract factory #2
      AbstractFactory factory2 = new ConcreteFactory2();
      Client c2 = new Client(factory2);
      c2.Run();

      // Wait for user input
      Console.Read();
    }
  }

  // "AbstractFactory"

  abstract class AbstractFactory
  {
    public abstract AbstractProductA CreateProductA();
    public abstract AbstractProductB CreateProductB();
  }

  // "ConcreteFactory1"

  class ConcreteFactory1 : AbstractFactory
  {
    public override AbstractProductA CreateProductA()
    {
      return new ProductA1();
    }
    public override AbstractProductB CreateProductB()
    {
      return new ProductB1();
    }
  }

  // "ConcreteFactory2"

  class ConcreteFactory2 : AbstractFactory
  {
    public override AbstractProductA CreateProductA()
    {
      return new ProductA2();
    }
    public override AbstractProductB CreateProductB()
    {
      return new ProductB2();
    }
  }

  // "AbstractProductA"

  abstract class AbstractProductA
  {
  }

  // "AbstractProductB"

  abstract class AbstractProductB
  {
    public abstract void Interact(AbstractProductA a);
  }

  // "ProductA1"

  class ProductA1 : AbstractProductA
  {
  }

  // "ProductB1"

  class ProductB1 : AbstractProductB
  {
    public override void Interact(AbstractProductA a)
    {
      Console.WriteLine(this.GetType().Name +
        " interacts with " + a.GetType().Name);
    }
  }

  // "ProductA2"

  class ProductA2 : AbstractProductA
  {
  }

  // "ProductB2"

  class ProductB2 : AbstractProductB
  {
    public override void Interact(AbstractProductA a)
    {
      Console.WriteLine(this.GetType().Name +
        " interacts with " + a.GetType().Name);
    }
  }

  // "Client" - the interaction environment of the products

  class Client
  {
    private AbstractProductA AbstractProductA;
    private AbstractProductB AbstractProductB;

    // Constructor
    public Client(AbstractFactory factory)
    {
      AbstractProductB = factory.CreateProductB();
      AbstractProductA = factory.CreateProductA();
    }

    public void Run()
    {
      AbstractProductB.Interact(AbstractProductA);
    }
  }
}



1.12  Example2

This real-world code demonstrates the creation of different animal worlds for a computer game using different factories. Although the animals created by the Continent factories are different, the interactions among the animals remain the same.




using System;

namespace Xpanxion.Structural.AbstractFactory
{
  // MainApp test application

    class MainApp
    {
        public static void Main()
        {
            // Create and run the Africa animal world
            ContinentFactory africa = new AfricaFactory();
            AnimalWorld world = new AnimalWorld(africa);
            world.RunFoodChain();

            // Create and run the America animal world
            ContinentFactory america = new AmericaFactory();
            world = new AnimalWorld(america);
            world.RunFoodChain();

            // Wait for user input
            Console.Read();
        }
    }

    // "AbstractFactory"

    abstract class ContinentFactory
    {
        public abstract Herbivore CreateHerbivore();
        public abstract Carnivore CreateCarnivore();
    }

    // "ConcreteFactory1"

    class AfricaFactory : ContinentFactory
    {
        public override Herbivore CreateHerbivore()
        {
            return new Wildebeest();
        }
        public override Carnivore CreateCarnivore()
        {
            return new Lion();
        }
    }

    // "ConcreteFactory2"

    class AmericaFactory : ContinentFactory
    {
        public override Herbivore CreateHerbivore()
        {
            return new Bison();
        }
        public override Carnivore CreateCarnivore()
        {
            return new Wolf();
        }
    }

    // "AbstractProductA"

    abstract class Herbivore
    {
    }

    // "AbstractProductB"

    abstract class Carnivore
    {
        public abstract void Eat(Herbivore h);
    }

    // "ProductA1"

    class Wildebeest : Herbivore
    {
    }

    // "ProductB1"

    class Lion : Carnivore
    {
        public override void Eat(Herbivore h)
        {
            // Eat Wildebeest
            Console.WriteLine(this.GetType().Name +
              " eats " + h.GetType().Name);
        }
    }

    // "ProductA2"

    class Bison : Herbivore
    {
    }

    // "ProductB2"

    class Wolf : Carnivore
    {
        public override void Eat(Herbivore h)
        {
            // Eat Bison
            Console.WriteLine(this.GetType().Name +
              " eats " + h.GetType().Name);
        }
    }

    // "Client"

    class AnimalWorld
    {
        private Herbivore herbivore;
        private Carnivore carnivore;

        // Constructor
        public AnimalWorld(ContinentFactory factory)
        {
            carnivore = factory.CreateCarnivore();
            herbivore = factory.CreateHerbivore();
        }

        public void RunFoodChain()
        {
            carnivore.Eat(herbivore);
        }
    }
}


1.13  Abstract Factory Design Pattern in ADO.NET 2.0

Abstract Factory is widely used in may application. Here we will see how Abstract Factory is used in ADO.NET 2.0.

ADO.NET 2.0 defines new set of abstract products classes such as DbConnection, DbCommand, DbParameter etc.... In the same time, each product has a concrete product class, like SqlConnection and OralceConnection etc... The XxxConnection classes inherit and implement DbConnection.

Also ADO.NET 2.0 brings new set of abstract factory classes to help us to implement Provider-Independent Data Access Layer. Through the use of DbProviderFactory Class which is an abstract factory class. The abstract factory -DbProviderFactory-declares set of CreateXxx Methods, like, CreateConnection, CreateCommand, CreateParamenter etc...

Each of these methods return a specific abstract product, for example CreateConnection returns DbConnection, CreateCommnad return DbCommand etc...

DbProviderFactory has set of concrete factory classes such as SqlClientFactory, OracleClientFactory etc...These concrete factories implements the CreateXxx methods to return specific products -provider specific classes.

Now have a look at the following UML Class Diagram and compare it with the Abstract Factory structure diagram:



Abstract Factory Participants:
·         AbstractFactory  (DbProviderFactory):
Role: Declares an interface for operations that create abstract product objects.
·         ConcreteFactory (SqlClientFactory, OracleClientFactory, OleDbFactory, OdbcFactory, Other Third-Party Factory):
Role: Implements the operations declared in the AbstractFactory to create concrete product objects.
·         AbstractProduct (DbConnection, DbCommand, DbParameter etc...):
Role: Declares an interface for a type of product object.
·         ConcreteProduct -Product- (SqlConnection, OracleConnection, XxxConnection, SqlCommand, XxxCommand...):
Role: Defines a product object to be created by the corresponding concrete factory and implements the AbstractProduct interface.
·         Client:
Role: Uses only interfaces declared by AbstractFactory and AbstractProduct classes.






using System;

namespace Xpanxion.Structural.AbstractFactory
  class MainApp
  {
    public static void Main()
    {
      DBProviderFactory sqlFactory = new SqlClientFactory();
      Client c1 = new Client(sqlFactory);
      c1.Run();


      DBProviderFactory oracleFactory = new OracleClientFactory();
      Client c2 = new Client(oracleFactory);
      c2.Run();
    }
  }


  class Client
  {
    private DbConnection dbConnection;
    private DbCommand dbCommand;

    // Constructor
    public Client(DBProviderFactory factory)
    {
      dbConnection = factory.CreateConnection();
      dbCommand = factory.CreateCommand();
    }   
  }
}


Hope this helps.

Thanks & Regards,
Arun Manglick

No comments:

Post a Comment