The Factory Pattern is based on the principle of separating the object creation logic from the code that uses the objects. Instead of directly instantiating objects using the new
keyword, a factory class or method is responsible for creating objects. This helps in hiding the complexity of object creation and provides a single point of control for object creation.
// Product interface
interface Shape {
void draw();
}
// Concrete products
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
class Square implements Shape {
@Override
public void draw() {
System.out.println("Drawing a square");
}
}
// Simple Factory class
class ShapeFactory {
public static Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
}
return null;
}
}
// Main class to test the factory
public class SimpleFactoryExample {
public static void main(String[] args) {
Shape circle = ShapeFactory.getShape("CIRCLE");
circle.draw();
Shape square = ShapeFactory.getShape("SQUARE");
square.draw();
}
}
In this example, the ShapeFactory
class is responsible for creating Shape
objects. The client code only needs to call the getShape
method with the desired shape type, without knowing the actual implementation details of object creation.
// Product interface
interface Animal {
void makeSound();
}
// Concrete products
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
// Creator class with a factory method
abstract class AnimalFactory {
public abstract Animal createAnimal();
public void animalSound() {
Animal animal = createAnimal();
animal.makeSound();
}
}
// Concrete creator classes
class DogFactory extends AnimalFactory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
class CatFactory extends AnimalFactory {
@Override
public Animal createAnimal() {
return new Cat();
}
}
// Main class to test the factory method
public class FactoryMethodExample {
public static void main(String[] args) {
AnimalFactory dogFactory = new DogFactory();
dogFactory.animalSound();
AnimalFactory catFactory = new CatFactory();
catFactory.animalSound();
}
}
In this example, the AnimalFactory
class has an abstract factory method createAnimal()
. The subclasses DogFactory
and CatFactory
override this method to create different types of Animal
objects.
null
: In a simple factory, if the input parameter is invalid, the factory method can return null
. However, this requires the client code to check for null
values.import java.util.HashMap;
import java.util.Map;
// Product interface
interface Vehicle {
void drive();
}
// Concrete products
class Car implements Vehicle {
@Override
public void drive() {
System.out.println("Driving a car");
}
}
class Bike implements Vehicle {
@Override
public void drive() {
System.out.println("Riding a bike");
}
}
// Factory class with caching
class VehicleFactory {
private static final Map<String, Vehicle> vehicleCache = new HashMap<>();
public static Vehicle getVehicle(String vehicleType) {
Vehicle vehicle = vehicleCache.get(vehicleType);
if (vehicle == null) {
if (vehicleType.equalsIgnoreCase("CAR")) {
vehicle = new Car();
} else if (vehicleType.equalsIgnoreCase("BIKE")) {
vehicle = new Bike();
}
if (vehicle != null) {
vehicleCache.put(vehicleType, vehicle);
}
}
return vehicle;
}
}
// Main class to test the factory with caching
public class FactoryCachingExample {
public static void main(String[] args) {
Vehicle car1 = VehicleFactory.getVehicle("CAR");
car1.drive();
Vehicle car2 = VehicleFactory.getVehicle("CAR");
car2.drive();
System.out.println(car1 == car2); // true, indicating the same object is reused
}
}
synchronized
methods or blocks.The Factory Pattern is a powerful design pattern in Java that helps in separating object creation logic from the client code. It provides a flexible and maintainable way to create objects, especially in complex systems. By using the different types of factory patterns (Simple Factory, Factory Method, and Abstract Factory) and following the common practices and best practices, developers can write more modular and robust code.