The Factory Method Pattern in Java is a creational design pattern that provides a structured approach to object creation by defining an interface or abstract class for creating objects, while allowing subclasses to decide which specific class to instantiate. Its primary purpose is to encapsulate the instantiation logic, promoting loose coupling between the creator and the objects it produces. Instead of directly calling a constructor (e.g., new ConcreteClass()), the pattern delegates the responsibility of object creation to a factory method, typically defined in an abstract class or interface. This method is then implemented by concrete subclasses, enabling flexibility and extensibility. For example, if you’re building an application that needs to create different types of shapes (like circles or squares), the Factory Method Pattern allows you to define a ShapeFactory with a method like createShape(), which concrete factories (e.g., CircleFactory, SquareFactory) can override to produce specific shape objects.
The problem the Factory Method Pattern attempts to solve is the tight coupling that arises when client code directly instantiates objects, making it difficult to modify or extend the system without altering existing code. Without this pattern, if you need to introduce a new type of object (e.g., adding a Triangle to a shape-drawing app), you’d have to update every place in the code where shapes are created, violating the Open/Closed Principle of SOLID design. By using the Factory Method Pattern, the client code interacts with an abstract creator type and relies on polymorphism, so new product types can be added by simply creating a new factory subclass without changing the client. This abstraction also enhances testability and maintainability, as the creation process can be easily mocked or swapped out, addressing the challenges of rigid, hard-coded dependencies in object-oriented systems.
This pattern introduces loose coupling between classes which is the most important principle one should consider when designing the application architecture.
Loose coupling can be introduced in application architecture by programming against abstract entities rather than concrete implementations. This makes the architecture more flexible. For our Factory Method Pattern in Java, we will begin with a enum type
CarType.java
, which will hold the different types of cars and will provide the car types to all other classes.
package com.java.creational.factorymethod;
public enum CarType {
SMALL, SEDAN, LUXURY
}
Car.java is the Super class of all car instances and it will contain the common logic applicable in creating cars of all types.
package com.java.creational.factorymethod;
public abstract class Car {
public Car(CarType model) {
this.model = model;
arrangeParts();
}
private void arrangeParts() {
// Do one time processing here
}
/* Do subclass level processing
in this method */
protected abstract void construct();
private CarType model = null;
public CarType getModel() {
return model;
}
public void setModel(CarType model) {
this.model = model;
}
}
LuxuryCar.java is a concrete implementation of the car type LUXURY.
package com.java.creational.factorymethod;
public class LuxuryCar extends Car {
LuxuryCar() {
super(CarType.LUXURY);
construct();
}
@Override
protected void construct() {
System.out.println("Building luxury car");
// add accessories
}
}
SmallCar.java is concrete implementation of car type SMALL
package com.java.creational.factorymethod;
public class SmallCar extends Car {
SmallCar() {
super(CarType.SMALL);
construct();
}
@Override
protected void construct() {
System.out.println("Building small car");
// add accessories
}
}
SedanCar.java is concrete implementation of car type SEDAN
package com.java.creational.factorymethod;
public class SedanCar extends Car {
SedanCar(){
super(CarType.SEDAN);
construct();
}
@Override
protected void construct() {
System.out.println("Building sedan car");
// add accessories
}
}
CarFactory.java is our main class implemented using factory pattern. It instantiates a car instance only after determining its type.
package com.java.creational.factorymethod;
public class CarFactory {
public static Car buildCar(CarType model) {
Car car = null;
switch (model) {
case SMALL:
car = new SmallCar();
break;
case SEDAN:
car = new SedanCar();
break;
case LUXURY:
car = new LuxuryCar();
break;
default:
// throw some exception
break;
}
return car;
}
}
In TestFactoryPattern.java, we will test our factory code. Lets run this class.
package com.java.creational.factorymethod;
public class TestFactoryPattern {
public static void main(String[] args) {
System.out.println(CarFactory.buildCar(CarType.SMALL));
System.out.println(CarFactory.buildCar(CarType.SEDAN));
System.out.println(CarFactory.buildCar(CarType.LUXURY));
}
}
Output:
Building small car
com.gofpatterns.creational.factory.SmallCar@2ef49ac3
Building sedan car
com.gofpatterns.creational.factory.SedanCar@3cdc904a
Building luxury car
com.gofpatterns.creational.factory.LuxuryCar@3485097d