Provide an interface for creating dependent objects without specifying their concrete classes.
Client software creates a concrete implementation of the abstract factory and then uses the generic interfaces to create the concrete objects that are part of the family of objects. The client does not know or care which concrete objects it gets from each of these concrete factories since it uses only the generic interfaces of their products.
The following figure shows an UML class diagram example for the Abstract Factory Design Pattern.
The descriptions of the classes are:
- AbstractFactory : Declares an interface for operations that create abstract product objects.
- ConcreteFactory : Implements the operations declared in the AbstractFactory to create concrete product objects.
- Product : Defines a product object to be created by the corresponding concrete factory and implements the AbstractProduct interface.
- Client : Uses only interfaces declared by AbstractFactory and AbstractProduct classes.
Where we use it ?
- The creation of objects should be independent of the system utilizing them.
- Systems should be capable of using multiple families of objects.
- Families of objects must be used together.
- Libraries must be published without exposing implementation details.
- Concrete classes should be decoupled from client
Tips for implementing
- As singletons: An Abstract Factory typically needs only one instance of a ConcreteFactory per product family. So it's usually best implemented as a
Singleton. - Creating the products: AbstractFactory only declares an interface for creating products. It's up to ConcreteProduct subclasses to actually create them. Thus, the most common way to do this is to define a Factory method for each product. A concrete factory will specify its products by overriding the factory method for each. While this implementation is simple, it requires a new concrete factory subclass for each product family, even if the product families differ only slightly.
If many product families are possible, the concrete factory can be implemented using the Prototype pattern. The Prototype-based approach eliminates the need for a new concrete factory class for each new product family. - Defining extensible factories: AbstractFactory usually defines a different operation for each kind of product it can produce. The kinds of products are encoded in the operation signatures. Adding a new kind of product requires changing the AbstractFactory interface and all the classes that depend on it.
A more flexible but less safe design is to add a parameter to operations that create objects. This parameter specifies the kind of object to be created. It could be a class identifier, an integer, a string, or anything else that identifies the kind of product. In fact with this approach, AbstractFactory only needs a single "public Object Make(KindObject type)" operation with a parameter indicating the kind of object to create.
Example
Suppose we want to build a global car factory. We need car factories in each location like IndiaCarFactory, USACarFactory and DefaultCarFactory. Each car factory can create different types of cars, such as MINI and LUXURY.
// got from http://www.geeksforgeeks.org/abstract-factory-pattern/
enum CarType
{
MINI, LUXURY
}
abstract class Car
{
Car(CarType model, Location location)
{
this.model = model;
this.location = location;
}
abstract void construct();
CarType model = null;
Location location = null;
CarType getModel()
{
return model;
}
void setModel(CarType model)
{
this.model = model;
}
Location getLocation()
{
return location;
}
void setLocation(Location location)
{
this.location = location;
}
@Override
public String toString()
{
return "CarModel - "+model + " located in "+location;
}
}
class LuxuryCar extends Car
{
LuxuryCar(Location location)
{
super(CarType.LUXURY, location);
construct();
}
@Override
protected void construct()
{
System.out.println("Connecting to luxury car");
}
}
class MiniCar extends Car
{
MiniCar(Location location)
{
super(CarType.MINI,location );
construct();
}
@Override
void construct()
{
System.out.println("Connecting to Mini car");
}
}
enum Location
{
DEFAULT, USA, INDIA
}
class INDIACarFactory
{
static Car buildCar(CarType model)
{
Car car = null;
switch (model)
{
case MINI:
car = new MiniCar(Location.INDIA);
break;
case LUXURY:
car = new LuxuryCar(Location.INDIA);
break;
default:
break;
}
return car;
}
}
class DefaultCarFactory
{
public static Car buildCar(CarType model)
{
Car car = null;
switch (model)
{
case MINI:
car = new MiniCar(Location.DEFAULT);
break;
case LUXURY:
car = new LuxuryCar(Location.DEFAULT);
break;
default:
break;
}
return car;
}
}
class USACarFactory
{
public static Car buildCar(CarType model)
{
Car car = null;
switch (model)
{
case MINI:
car = new MiniCar(Location.USA);
break;
case LUXURY:
car = new LuxuryCar(Location.USA);
break;
default:
break;
}
return car;
}
}
class CarFactory
{
private CarFactory() { }
public static Car buildCar(CarType type, Location location)
{
Car car = null;
switch(location)
{
case USA:
car = USACarFactory.buildCar(type);
break;
case INDIA:
car = INDIACarFactory.buildCar(type);
break;
default:
car = DefaultCarFactory.buildCar(type);
}
return car;
}
}
class AbstractDesign
{
public static void main(String[] args)
{
System.out.println(CarFactory.buildCar(CarType.MINI, Location.INDIA));
System.out.println(CarFactory.buildCar(CarType.LUXURY, Location.USA));
}
}
Advantages
-
It 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.
-
It makes exchanging product families easy. The class of a concrete factory appears only once in an application. It can use different product configurations simply by changing the concrete factory.
-
It promotes consistency among products.
-
Supporting new kinds of products is difficult. 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.
Disadvantages
- Difficult to support new kind of products: 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
This is a totally educational post.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
References
Book : Design Patterns Elements of Reusable Object-Oriented Software