The Abstract Factory Design Pattern is a creational design pattern often referred to as a "Factory of Factories". It provides an interface for creating families of related or dependent objects without specifying their concrete classes.
Instead of creating objects directly using concrete constructors, client code requests objects from an abstract factory interface, ensuring that related objects (such as UI buttons and scrollbars for a specific OS) are always created together consistently.
Essential Key Points to Remember
Family of Products: Abstract Factory focuses on creating entire families of related products rather than single individual instances.
Loose Coupling: The client program is completely decoupled from concrete implementation classes. It interacts solely through abstract interfaces.
Open/Closed Principle: You can introduce new variants of product families into your application without breaking existing client code.
Single Responsibility Principle: The object creation logic is centralized in dedicated factory classes, simplifying maintenance and testing.
Consistency Across Products: Guarantees that products created within the same family are compatible with each other.
When Should You Use It?
The Abstract Factory pattern is ideal when:
A system must be independent of how its products are created, composed, and represented.
A system needs to be configured with one of multiple families of products (e.g., cross-platform UI themes, multi-cloud storage adapters).
A family of related product objects is designed to be used together, and you need to enforce this constraint at runtime.
The Abstract Factory Pattern acts as a "super-factory" or "factory of factories." In this practical implementation, we organize related objects into concrete families based on their environment:
Land Animal Family: Produces land animals such as Dog, Cat, and Lion.
Sea Animal Family: Produces aquatic animals such as Shark and Octopus.
Abstract Animal Factory: The super factory that delegates the creation of the correct concrete factory based on input.
Complete Java Code Implementation
Here is the complete source code demonstrating the interfaces, concrete implementations, factory classes, and client execution:
// 1. Common Product Interface
interface Animal {
String speak();
}
// Concrete Products: Land Animals
class Dog implements Animal {
@Override
public String speak() { return "Bark Bark"; }
}
class Cat implements Animal {
@Override
public String speak() { return "Meow Meow"; }
}
class Lion implements Animal {
@Override
public String speak() { return "Roar"; }
}
// Concrete Products: Sea Animals
class Shark implements Animal {
@Override
public String speak() { return "Cannot Speak"; }
}
class Octopus implements Animal {
@Override
public String speak() { return "Squawk"; }
}
// 2. Abstract Factory (Super Factory)
abstract class AnimalFactory {
public abstract Animal getAnimal(String animalType);
public static AnimalFactory getAnimalFactory(String factoryType) {
if ("SEA".equalsIgnoreCase(factoryType)) {
return new SeaAnimalFactory();
} else if ("LAND".equalsIgnoreCase(factoryType)) {
return new LandAnimalFactory();
}
return null;
}
}
// 3. Concrete Factories
class LandAnimalFactory extends AnimalFactory {
@Override
public Animal getAnimal(String animalType) {
if ("DOG".equalsIgnoreCase(animalType)) {
return new Dog();
} else if ("CAT".equalsIgnoreCase(animalType)) {
return new Cat();
} else if ("LION".equalsIgnoreCase(animalType)) {
return new Lion();
}
return null;
}
}
class SeaAnimalFactory extends AnimalFactory {
@Override
public Animal getAnimal(String animalType) {
if ("SHARK".equalsIgnoreCase(animalType)) {
return new Shark();
} else if ("OCTOPUS".equalsIgnoreCase(animalType)) {
return new Octopus();
}
return null;
}
}
// 4. Client / Driver Application
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
// Fetch Sea Animal Factory
AnimalFactory seaFactory = AnimalFactory.getAnimalFactory("SEA");
Animal shark = seaFactory.getAnimal("SHARK");
System.out.println("Shark says: " + shark.speak());
Animal octopus = seaFactory.getAnimal("OCTOPUS");
System.out.println("Octopus says: " + octopus.speak());
// Fetch Land Animal Factory
AnimalFactory landFactory = AnimalFactory.getAnimalFactory("LAND");
Animal dog = landFactory.getAnimal("DOG");
System.out.println("Dog says: " + dog.speak());
Animal cat = landFactory.getAnimal("CAT");
System.out.println("Cat says: " + cat.speak());
Animal lion = landFactory.getAnimal("LION");
System.out.println("Lion says: " + lion.speak());
}
}
Key Takeaways for Developers
Encapsulation: The client application never calls the concrete class constructor directly using new.
Extensibility: To add a new environment (e.g., SkyAnimalFactory with Bird), simply create the classes and register them in the super-factory without modifying existing product hierarchies.
Consistency: Ensures that related products stay within their defined family domain.
The Abstract Factory Pattern belongs to the Creational Design Pattern family. At its simplest, it is defined as a "Super Factory" or a "Factory of Factories" because it creates and returns other factories.
When your application needs to produce multiple distinct families of related products without hardcoding the exact concrete classes into the client code, the Abstract Factory pattern provides the perfect abstraction layer.
Real-World Analogy: Animal Ecosystem
To understand how this hierarchy functions conceptually, consider dividing animals into two distinct habitats:
Land Animals Family: Animals like Dog, Cat, and Lion share land-based behavior and characteristics.
Sea Animals Family: Aquatic animals like Shark and Octopus have aquatic life cycles and navigation.
Abstract Animal Factory: The master factory that determines whether to supply a LandAnimalFactory or a SeaAnimalFactory based on the client's request.
Core Benefits of Abstract Factory
Isolates Concrete Classes: Client code only knows about the product interfaces, shielding your business logic from implementation changes.
Promotes Consistency: Enforces that products created from a single factory family are designed to work harmoniously together.
Adheres to Open/Closed Principle: Adding a brand-new factory family (such as AirAnimalFactory) requires no invasive modifications to the existing client execution code.