Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
When to use
- To support families of related or dependent objects.
- To encapsulate platform dependencies to make an application portable.
- To prevent client code from using the 'new' operator.
- To easily swap the underlying platform with minimal changes.
Intent
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
Components
- An Abstract Factory class (public)
- Factory Implementations for various familes (protected)
- Interfaces for various products (public)
- Set of product implementations for various families (protected)
Structure
Implementation
1 Define interfaces for different types products/objects. Each family will have all these parts.
package com.gaurav.abstractfactory;
public interface Engine {
public void design();
public void manufacture();
public void test();
}
package com.gaurav.abstractfactory;
public interface Tyre {
public void design();
public void manufacture();
}
2 Create sets of implementation subclasses for the above interfaces. Classes are access protected to prohibit instantiations in client modules using the 'new' operator.
package com.gaurav.abstractfactory;
class CarEngine implements Engine {
@Override
public void design() {
System.out.println("Designing Car Engine");
}
@Override
public void manufacture() {
System.out.println("Manufacturing Car Engine");
}
@Override
public void test() {
System.out.println("Testing Car Engine");
}
}
package com.gaurav.abstractfactory;
class TruckEngine implements Engine {
@Override
public void design() {
System.out.println("Designing Truck Engine");
}
@Override
public void manufacture() {
System.out.println("Manufacturing Truck Engine");
}
@Override
public void test() {
System.out.println("Testing Truck Engine");
}
}
package com.gaurav.abstractfactory;
class CarTyre implements Tyre {
@Override
public void design() {
System.out.println("Designing Car Tyre");
}
@Override
public void manufacture() {
System.out.println("Manufacturing Car Tyre");
}
}
package com.gaurav.abstractfactory;
class TruckTyre implements Tyre {
@Override
public void design() {
System.out.println("Designing Truck Tyre");
}
@Override
public void manufacture() {
System.out.println("Manufacturing Truck Tyre");
}
}
3 Create a Abstract Factory class with factory method 'getFactory()'. Clients can use this method to get an object the required factory. This example uses both Singleton and Factory Method patterns for better design.
package com.gaurav.abstractfactory;
public abstract class Factory {
/* Singleton Factory objects */
private static Factory carFactory = null;
private static Factory truckFactory = null;
public abstract Engine getEngine();
public abstract Tyre getTyre();
/*
* This is the factory method exposed to the client.
* Client requests for a factory instance by passing the type.
* Client does not need to know about which & how
* object is created internally.
*/
public static Factory getFactory(String vehicleType)
throws UnknownVehicleException {
if (vehicleType == null) {
return null;
}
Factory factory = null;
switch (vehicleType) {
case "car":
if (carFactory == null)
carFactory = new CarFactory();
factory = carFactory;
break;
case "truck":
if (truckFactory == null)
truckFactory = new TruckFactory();
factory = truckFactory;
break;
default:
throw new UnknownVehicleException();
}
return factory;
}
}
4 Create Factory implementations. Classes are protected to prohibit direct access in client modules.
package com.gaurav.abstractfactory;
class CarFactory extends Factory {
@Override
public Engine getEngine() {
return new CarEngine();
}
@Override
public Tyre getTyre() {
return new CarTyre();
}
}
package com.gaurav.abstractfactory;
public class TruckFactory extends Factory {
TruckFactory() {}
@Override
public Engine getEngine() {
return new TruckEngine();
}
@Override
public Tyre getTyre() {
return new TruckTyre();
}
}
5 The client code. Client is exposed to only the Abstract Factory class and the interfaces.
package com.gaurav.client;
import java.util.Scanner;
import com.gaurav.abstractfactory.Engine;
import com.gaurav.abstractfactory.Factory;
import com.gaurav.abstractfactory.Tyre;
import com.gaurav.abstractfactory.UnknownVehicleException;
public class AbstractFactoryClient {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String vehicleType = in.nextLine().toLowerCase();
/* Get the factory instance */
Factory factory;
try {
factory = Factory.getFactory(vehicleType);
/* Get the Engine from the factory */
Engine engine = factory.getEngine();
engine.design();
engine.manufacture();
engine.test();
/* Get the Tyre from the factory */
Tyre tyre = factory.getTyre();
tyre.design();
tyre.manufacture();
} catch (UnknownVehicleException e) {
System.out.println("Invalid vehicle type entered!");
}
in.close();
}
}
Output
[input1]
Car
[output1]
Designing Car Engine
Manufacturing Car Engine
Testing Car Engine
Designing Car Tyre
Manufacturing Car Tyre
[input2]
Bus
[output2]
Invalid vehicle type entered!
Benefits
- Loosely coupled code.
- Abstract Factory provides a single point of access for all products in a family.
- New product family can be easily supported.
Drawbacks
- More layers of abstraction increases complexity.
- If there are any changes to any underlying detail of one factory, the interface might need to be modified for all the factories.
Real World Examples
Providing data access to two different data sources (e.g. a SQL Database and a XML file). You have two different data access classes (a gateway to the datastore). Both inherit from a base class that defines the common methods to be implemented (e.g. Load, Save, Delete). Which data source shall be used shouldn't change the way client code retrieves it's data access class. Your Abstract Factory knows which data source shall be used and returns an appropriate instance on request. The factory returns this instance as the base class type.
Software Examples
Dependency Injection
Java SDK Examples
javax.xml.parsers.DocumentBuilderFactory newInstance()
javax.xml.transform.TransformerFactory newInstance()
javax.xml.xpath.XPathFactory newInstance()
Top comments (3)
I like it,
But For some reason I find it bit cumbersome if I want add new factory, I will have to add this to support in Factory class and add a switch case , or maybe some dependency injection can be done?
Hey, CarEngine implementation is repeated instead of TruckEngine implementation.
Updated the blog. Thanks for pointing it out.