Abstract Factory
Problem
The creation of different family of objects is encapsulated by lots of #ifdef statements which spread like rabbits throughout the code
#ifdef MYCLASS_FAMILY_1
MyClassA *myClassA = new MyClassA1();
MyClassB *myClassB = new MyClassB1();
#elif MYCLASS_FAMILY_2
MyClassA *myClassA = new MyClassA2();
MyClassB *myClassB = new MyClassB2();
#endif
Intent
Provide an interface for creating families of related or dependent or suite of objects without specifying their concrete classes.
The new operator considered harmful.
Applicability
- a system should be independent of how its products are created, composed, and represented.
- a system should be configured with one of multiple families of products.
- a family of related product objects is designed to be used together, and you need to enforce this constraint. e.g. family will give only the objects of MyClassA and MyClassB both, neither less nor more.
- you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations.
Structure
Participants
- AbstractFactory (MyClassFactory)
Declares an interface for operations thats create abstract MyClass objects.
- ConcreteFactory (MyClassFactory1, MyClassFactory2)
Implements the operations to create concrete MyClass objects.
- AbstractMyClass (MyClassA, MyClassB)
declares an interface for a type ofproduct object.
- ConcreteMyClass (MyClassA1, MyClassB1)
defines a product object to be created by the corresponding concrete factory.
implements the AbstractMyClass interface.
- Client
uses only interfaces declared by AbstractFactory and AbstractMyClass classes.
Thumb Rules
- It defers creation of product objects to its ConcreteFactory sub-class.
- It isolates clients from implementation\concrete classes. The class names, MyClassA1, do not apear in client code.
- It makes exchanging families easy. The instance needs to be changed from MyClassFactory1 to MyClassFactory2.
- It promotes consistancy among products. The MyClass objects designed to work together in a family are together. MyClassA1 and MyClassB1 stay together.
- Supporting new kinds of MyClass is difficult, e.g. MyClassC.
The Abstract factory interface is fixed with a factory method for each MyClass.
A more flexible but less safe design is to add a parameter to operations that create objects. Only a single "Make" operation with a parameter indicating the kind of object to create is required. This is the technique used in prototype and class based abstract factories.
Valid only when all objects have the same abstract base class, as Make() will return the value of same type. Next problem is that the client doesn't have knowledge of the sub-class of returned object so valid sub-class functions are ambiguous.
- A concrete factory is often a singleton.
- They are often implemented with factory methods. It requires a new concrete factory subclass for each product family.
They can also be implemented using Prototype, if many product families are possible. The new concrete factory subclass for each product family won't be required.
Example
The purpose of the Abstract Factory is to provide an interface for creating families of related objects, without specifying concrete classes. This pattern is found in the sheet metal stamping equipment used in the manufacture of Japanese automobiles. The stamping equipment is an Abstract Factory which creates auto body parts. The same machinery is used to stamp right hand doors, left hand doors, right front fenders, left front fenders, hoods, etc. for different models of cars. Through the use of rollers to change the stamping dies, the concrete classes produced by the machinery can be changed within three minutes.
Code - Before
The client creates “MyClass” objects directly, and must embed all possible permutations in nasty looking code.
#define MYCLASS_A
class MyClass {
public:
virtual void print() = 0;
};
class MyClassA1: public MyClass{
public:
void print() { cout << "MyClassA1\n"; }
};
class MyClassA2: public MyClass{
public:
void print() { cout << "MyClassA2\n"; }
};
class MyClassB1: public MyClass{
public:
void print() { cout << "MyClassB1\n"; }
};
class MyClassB2: public MyClass{
public:
void print() { cout << "MyClassB2\n"; }
};
void display_myclass_one() {
#ifdef MYCLASS_A
MyClass* myClass[] = { new MyClassA1,
new MyClassA2};
#else // MYCLASS_B
MyClass* myClass[] = { new MyClassB1,
new MyClassB2};
#endif
myClass[0]->print(); myClass[1]->print();
}
void display_myclass_two() {
#ifdef MYCLASS_A
MyClass* myClass[] = { new MyClassA2,
new MyClassA1};
#else // MYCLASS_B
MyClass* myClass[] = { new MyClassB2,
new MyClassB1};
#endif
myClass[0]->print(); myClass[1]->print();
}
int main() {
#ifdef MYCLASS_A
MyClass* myClass = new MyClassA1;
#else // MYCLASS_B
MyClass* myClass = new MyClassB1;
#endif
myClass->print();
display_myclass_one();
display_myclass_two();
}
MyClassA1
MyClassA1
MyClassA2
MyClassA2
MyClassA1
Code - After
The client: creates a specific “factory” object, is careful to eschew use of “new”, and delegates all creation requests to the factory.
#define MYCLASS_B
class MyClass {
public:
virtual void print() = 0;
};
class MyClassA1 : public MyClass {
public:
void print() { cout << "MyClassA1\n"; }
};
class MyClassA2 : public MyClass {
public:
void print() { cout << "MyClassA2\n"; }
};
class MyClassB1 : public MyClass {
public:
void print() { cout << "MyClassB1\n"; }
};
class MyClassB2 : public MyClass {
public:
void print() { cout << "MyClassB2\n"; }
};
class Factory {
public:
virtual MyClass* create_1() = 0;
virtual MyClass* create_2() = 0;
};
class MyClassAFactory : public Factory {
public:
MyClass* create_1() {
return new MyClassA1; }
MyClass* create_2() {
return new MyClassA2; }
};
class MyClassBFactory : public Factory {
public:
MyClass* create_1() {
return new MyClassB1; }
MyClass* create_2() {
return new MyClassB2; }
};
Factory* factory;
void display_myclass_one() {
MyClass* myClass[] = { factory->create_1(),
factory->create_2() };
myClass[0]->print(); w[1]->print();
}
void display_myclass_two() {
MyClass* myClass[] = { factory->create_2(),
factory->create_1() };
myClass[0]->print(); myClass[1]->print();
}
int main() {
#ifdef MYCLASS_A
factory = new MyClassAFactory;
#else // MYCLASS_B
factory = new MyClassBFactory;
#endif
MyClass* myClass = factory->create_1();
myClass->print();
display_myclass_one();
display_myclass_two();
}
MyClassB1
MyClassB1
MyClassB2
MyClassB2
MyClassB1