Here I will explain the difference between Factory Method & Abstract Factory design patterns. If you want to know about difference between Simple Factory & Factory Method, you can check my previous post.
Factory Method:
As explained in previous post, Factory Method can be used when same standard process or set of business rules needs to be applied on the newly created objects that share common super type. Basically we define an abstract factory class (Java abstract class, not the design pattern) which encapsulates the internal steps of the standard process that need to be applied on the newly created objects. In that class, we define an abstract method that returns common super type of the product. This abstract method is implemented by the concrete subclasses of the abstract factory class. I will go back to the example which I used in my previous post.
public interface ElectronicToy {
public void switchOn();
public void switchOff();
}
public class Gun implements ElectronicToy {
public void switchOn() {
System.out.println("Gun on");
}
public void switchOff() {
System.out.println("Gun off");
}
}
public class StandardGun extends Gun {
}
public class LuxuryGun extends Gun {
}
public class Doll implements ElectronicToy {
public void switchOn() {
System.out.println("Play");
}
public void switchOff() {
System.out.println("Stop");
}
}
public class StandardDoll extends Doll {
}
public class LuxuryDoll extends Doll {
}
public abstract class ToyFactory {
public void order(String type) {
ElectronicToy toy = makeToy(type);
pack(toy);
ship(toy);
}
public abstract ElectronicToy makeToy(type);
public void pack(ElectronicToy toy) {
System.out.println("packed");
}
public void ship(ElectronicToy toy) {
System.out.println("shipped");
}
}
public class StandardToyFactory extends ToyFactory {
public ElectronicToy makeToy(type) {
if ("gun".equals(type)) {
return new StandardGun();
} else if ("doll".equals(type)) {
return new StandardDoll();
}
}
}
public class LuxuryToyFactory extends ToyFactory{
public ElectronicToy makeToy(type) {
if("gun".equals(type)){
return new LuxuryGun();
} else if("doll".equals(type)){
return new LuxuryDoll();
}
}
}
As you can see above, we have two different concrete implementations of the ToyFactory, one makes standard toys & the other makes luxury toys. But the order process (make, pack & ship) remains same as both belong to the same company. This pattern enforces standard business rules that need to be applied on the newly created objects.
Abstract Factory:
Abstract Factory pattern provides an interface to create a group of related objects. Let’s take an example below.
To actually create the ElectricToy object we need some parts like plastic body, circuit (to switch on/off electricity) etc. But the materials will be different for standard toys & luxury toys. So we define an interface named ElectricToyPartsFactory which will create all the related objects that are required to make a particular type of electric toy (standard or luxury).
public interface ElectricToyPartsFactory {
public Circuit makeCircuit();
public PlasticBody makeBody();
}
Now we create two concrete classes that implements the above interface.
public class StandardElectricToyPartsFactory extends ElectricToyPartsFactory {
public Circuit makeCircuit() {
return new StandardCicuit();
}
public PlasticBody makeBody() {
return new StandardPlasticBody();
}
}
public class PremiumElectricToyPartsFactory ElectricToyPartsFactory {
public Circuit makeCircuit() {
return new PremiumCicuit();
}
public PlasticBody makeBody() {
return new PremiumPlasticBody();
}
}
So now we have two concrete classes that groups together two different types of parts (standard & premium) required for each toy factory. Let’s update our previous ToyFactory subclasses.
public class StandardToyFactory extends ToyFactory {
StandardElectricToyPartsFactory partsFactory = new StandardElectricToyPartsFactory();
public ElectronicToy makeToy(type) {
if ("gun".equals(type)) {
return new StandardGun(partsFactory);
} else if ("doll".equals(type)) {
return new StandardDoll(partsFactory);
}
}
}
public class LuxuryToyFactory extends ToyFactory {
PremiumElectricToyPartsFactory partsFactory = new PremiumElectricToyPartsFactory();
public ElectronicToy makeToy(type) {
if ("gun".equals(type)) {
return new LuxuryGun(partsFactory);
} else if ("doll".equals(type)) {
return new LuxuryDoll(partsFactory);
}
}
}
The StandardGun class can actually look like below:
public class StandardGun implements ElectronicToy {
private Circuit circuit;
private PlasticBody body;
public Gun(ElectricToyPartsFactory partsFactory) {
this.circuit = partsFactory.makeCircuit();
this.body = partsFactory.makeBody();
}
public void switchOn() {
System.out.println("Gun on");
}
public void switchOff() {
System.out.println("Gun off");
}
}
By doing this we achieved couple of things:
- We made sure that the product & the parts that it is made of are decoupled. Product (toy) doesn’t need to know about the concrete type of parts. It is supplied when the toy is manufactured. That way we can just switch the partsFactory subclass as needed. It will require minimal change in the existing code.
- We have grouped the related parts in a single factory. It is less error prone. As same partsFactory object is supplying all the related parts required to make the toy.
Summary:
Summarizing the difference between Factory Method & Abstract Factory design patterns:
- Abstract Factory design pattern just creates a family of related objects. It doesn’t enforce business rules on the newly created object like Factory Method pattern.
- If we think a bit, we can see that all methods in the Abstract Factory pattern are factory methods. Abstract Factory uses Factory Method pattern & defines a bunch of factory methods within the factory interface.