Friday, March 12, 2010

Factory design pattern

Let us give ourselves godlike powers and create a factory to produce animals. The basic structure for such a factory would look like:

package raj;

/**
* @author Rajeev
*/
public class AnimalFactory {
private String animalType;
AnimalFactory(String animalType) {
this.animalType = animalType;}

public Animal createAnimal() {
if (animalType.equals("Dog")) return new Dog();
else if (animalType.equals("Cat")) return new Cat();
else if (animalType.equals("Donkey")) return new Donkey();
else if (animalType.equals("Horse")) return new Horse();
else return new Animal();
}
}

class Animal {public String toString(){return "Animal";}}
class Dog extends Animal {public String toString(){return "Dog";}}
class Cat extends Animal {public String toString(){return "Cat";}}
class Donkey extends Animal {public String toString(){return "Donkey";}}
class Horse extends Animal {public String toString(){return "Horse";}}

It allows us to create an animal of a type which the factory has been geared to produce. We can test it with:

package raj;

/**
* @author Rajeev
*/
public class TestFactory {

public static void main(String[] arg) {
// create factories
AnimalFactory horseFactory = new AnimalFactory("Horse");
AnimalFactory dogFactory = new AnimalFactory("Dog");
AnimalFactory catFactory = new AnimalFactory("Cat");
AnimalFactory donkeyFactory = new AnimalFactory("Donkey");
AnimalFactory animalFactory = new AnimalFactory("");
// create horses
Animal animal = horseFactory.createAnimal();
Animal animal2 = horseFactory.createAnimal();
System.out.printf("We are type %s %s%n",animal,animal2);
// create dogs
animal = dogFactory.createAnimal();
animal2 = dogFactory.createAnimal();
System.out.printf("We are type %s %s%n",animal,animal2);
// create cats
animal = catFactory.createAnimal();
animal2 = catFactory.createAnimal();
System.out.printf("We are type %s %s%n",animal,animal2);
// create donkeys
animal = donkeyFactory.createAnimal();
animal2 = donkeyFactory.createAnimal();
System.out.printf("We are type %s %s%n",animal,animal2);
// create animals
animal = animalFactory.createAnimal();
animal2 = animalFactory.createAnimal();
System.out.printf("We are type %s %s%n",animal,animal2);

}
}

Now if we decide to produce 'sheep' in future or make a species extinct then all we have to do is to alter the Factory class to incorporate this new requirement at one central place. A demigod could be allocated a particular factory type now and he can happily go on churning objects of that particular species. This programming idiom is useful but not the Factory method pattern defined by GoF. Our current UML diagram is shown in Fig 1.


The GoF Factory method design pattern would alter the diagram to Fig 2.
With this variation, the TestFactory client is totally isolated from the realisation of the factory and its products (the concrete Animal). In this approach the client talks to the abstract interfaces which delegate the actual implementation to their subclasses. Our code changes accordingly to make this possible.

package raj;

public class TestFactoryFM {

public static void main(String[] arg) {
// create factory
AnimalFactoryIF horseFactory = new AnimalFactory("Horse");
// create horses
AnimalIF animal = horseFactory.createAnimal();
AnimalIF animal2 = horseFactory.createAnimal();
System.out.printf("We are type %s %s%n",animal,animal2);}
}

abstract class AnimalFactoryIF {abstract public AnimalIF createAnimal();}
class AnimalFactory extends AnimalFactoryIF {
private String animalType;
AnimalFactory(String animalType) {
this.animalType = animalType;}

public AnimalIF createAnimal() {
if (animalType.equals("Dog")) return new Dog();
else if (animalType.equals("Cat")) return new Cat();
else if (animalType.equals("Donkey")) return new Donkey();
else if (animalType.equals("Horse")) return new Horse();
else return new Animal();
}
}

abstract class AnimalIF { abstract public String toString();};
class Animal extends AnimalIF {public String toString(){return "Animal";}}
class Dog extends Animal {public String toString(){return "Dog";}}
class Cat extends Animal {public String toString(){return "Cat";}}
class Donkey extends Animal {public String toString(){return "Donkey";}}
class Horse extends Animal {public String toString(){return "Horse";}}

Hitherto, in our approach we have one-to-one relationship between the AnimalIF and its subclass and the FactoryIF and its subclass. However, the power of the approach becomes apparent if subclass our FactoryIF with another factory, SpecialFactory, which produces superior quality animals, SpecialAnimal, which are a subclass of the AnimalIF. The client is built to an interface and is totally unaware of the type of animal it is dealing with. Unsurprisingly, the Factory method design is used frequently in frameworks.

No comments:

Post a Comment