Introduction to Factory Design Patterns
Factory patterns are creational design patterns that provide an interface for creating objects while allowing subclasses to determine which class to instantiate. This approach delegates object instantiation to subclasses, enabling client code to be decoupled from specific concrete classes without requiring modifications to the client.
Core Components
- Factory Interface: Defines the method for object creation, to be implemented by concrete factories.
- Concrete Factory: Implements the factory interface and is responsible for creating specific product objects.
- Product Interface: Specifies the interface for the objects the factory creates.
- Concrete Product: Implements the product interface and represents the objects created by the factory.
Pattern Variants
- Simple Factory: A single factory class creates instances based on input parameters. This approach can violate the open-closed principle since the factory class must be modified when new product types are added.
- Factory Method: Defines an interface for object creation but lets subclasses decide which class to instantiate. This adheres to the open-closed principle, allowing system extension by adding new concrete factories.
- Abstract Factory: Provides an interface for creating families of related or dependent objects without specifying their concrete classes. This pattern is typically implemented with multiple factory classes, each responsible for a product family.
Key Benefits
- Encapsulates object creation logic, allowing clients to focus on product interfaces rather than implementation specifics.
- Enhances code extensibility by facilitating the addition of new factory or product classes.
Common Use Cases
- When a class cannot anticipate the type of objects it must create.
- When a class delegates creation responsibilities to its subclasses.
- When dynamic determination of object instantiation is required among multiple helper subclasses.
Simple Factory Example
The following demonstrates a basic shape creation system using a simple factory pattern:
// Shape interface
interface Shape {
void render();
}
// Concrete shape implementations
class CircleShape implements Shape {
@Override
public void render() {
System.out.println("Drawing a circle.");
}
}
class RectangleShape implements Shape {
@Override
public void render() {
System.out.println("Drawing a rectangle.");
}
}
class SquareShape implements Shape {
@Override
public void render() {
System.out.println("Drawing a square.");
}
}
// Simple factory class
class ShapeMaker {
public static Shape produceShape(String shapeType) {
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new CircleShape();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new RectangleShape();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new SquareShape();
} else {
throw new IllegalArgumentException("Unknown shape type: " + shapeType);
}
}
}
// Client implementation
class ShapeClient {
public static void main(String[] args) {
Shape circle = ShapeMaker.produceShape("CIRCLE");
circle.render();
Shape rectangle = ShapeMaker.produceShape("RECTANGLE");
rectangle.render();
Shape square = ShapeMaker.produceShape("SQUARE");
square.render();
}
}
Factory Method Example
This example illustrates a document processing system using the factory method pattern:
// Document interface
interface Doc {
void openFile();
void saveFile();
}
// Concrete document types
class PdfDoc implements Doc {
@Override
public void openFile() {
System.out.println("Opening PDF document.");
}
@Override
public void saveFile() {
System.out.println("Saving PDF document.");
}
}
class WordDoc implements Doc {
@Override
public void openFile() {
System.out.println("Opening Word document.");
}
@Override
public void saveFile() {
System.out.println("Saving Word document.");
}
}
// Abstract creator
interface DocCreator {
Doc generateDocument();
}
// Concrete creators
class PdfCreator implements DocCreator {
@Override
public Doc generateDocument() {
return new PdfDoc();
}
}
class WordCreator implements DocCreator {
@Override
public Doc generateDocument() {
return new WordDoc();
}
}
// Client implementation
class DocClient {
public static void main(String[] args) {
DocCreator pdfFactory = new PdfCreator();
Doc pdfDocument = pdfFactory.generateDocument();
pdfDocument.openFile();
pdfDocument.saveFile();
DocCreator wordFactory = new WordCreator();
Doc wordDocument = wordFactory.generateDocument();
wordDocument.openFile();
wordDocument.saveFile();
}
}
Abstract Factory Example
This example shows a computer manufacturing system using the abstract factory pattern:
// Abstract product interfaces
interface PortableComputer {
void showInfo();
}
interface StationaryComputer {
void displayInfo();
}
// Concrete products for Dell
class DellLaptop implements PortableComputer {
@Override
public void showInfo() {
System.out.println("Dell Laptop product.");
}
}
class DellDesktop implements StationaryComputer {
@Override
public void displayInfo() {
System.out.println("Dell Desktop product.");
}
}
// Concrete products for HP
class HPLaptop implements PortableComputer {
@Override
public void showInfo() {
System.out.println("HP Laptop product.");
}
}
class HPDesktop implements StationaryComputer {
@Override
public void displayInfo() {
System.out.println("HP Desktop product.");
}
}
// Abstract factory interface
interface ComputerManufacturer {
PortableComputer buildLaptop();
StationaryComputer assembleDesktop();
}
// Concrete factories
class DellManufacturer implements ComputerManufacturer {
@Override
public PortableComputer buildLaptop() {
return new DellLaptop();
}
@Override
public StationaryComputer assembleDesktop() {
return new DellDesktop();
}
}
class HPManufacturer implements ComputerManufacturer {
@Override
public PortableComputer buildLaptop() {
return new HPLaptop();
}
@Override
public StationaryComputer assembleDesktop() {
return new HPDesktop();
}
}
// Client implementation
class ComputerClient {
public static void main(String[] args) {
ComputerManufacturer dellFactory = new DellManufacturer();
PortableComputer dellLaptop = dellFactory.buildLaptop();
StationaryComputer dellDesktop = dellFactory.assembleDesktop();
dellLaptop.showInfo();
dellDesktop.displayInfo();
ComputerManufacturer hpFactory = new HPManufacturer();
PortableComputer hpLaptop = hpFactory.buildLaptop();
StationaryComputer hpDesktop = hpFactory.assembleDesktop();
hpLaptop.showInfo();
hpDesktop.displayInfo();
}
}
Practical Applications
Factory patterns are widely implemented in software frameworks and libraries. Notable examples include:
- JDBC DriverManager: Uses factory principles to dynamically load appropriate database drivers based on connection strings.
- Spring Framework: Employs factory patterns extensively through its
BeanFactoryandApplicationContextinterfaces, which manage object creation and dependency injection within the Inversion of Control container.