← Back to Advanced Java Course | Chapter 6: Design Patterns | Lesson 3 of 14

Java Factory Pattern

What is the Factory Pattern?

The Factory Pattern defines an interface, or an abstract method, for creating an object, but lets subclasses decide exactly which concrete class actually gets instantiated. This keeps the calling code working purely against the interface, with no knowledge of which implementation it ultimately receives.

Example: What is the Factory Pattern?

java
public class Main {
	interface Shape {}
	static class Circle implements Shape {}
	interface ShapeFactory { Shape create(); }
	public static void main(String[] args) {
		ShapeFactory factory = Circle::new; // caller only knows the interface
		Shape shape = factory.create();
		System.out.println(shape.getClass().getSimpleName());
	}
}

Polymorphic Factory Method

By implementing a factory method inside an abstract class and leaving it abstract there, each subclass can override that method to specify which concrete class gets created for its particular variant, letting the object-creation logic vary polymorphically alongside the rest of the subclass's behavior.

Example: Polymorphic Factory Method

java
public class Main {
	static abstract class Dialog {
		abstract Object createButton(); // subclasses decide the concrete type
	}
	static class WindowsDialog extends Dialog {
		Object createButton() { return "WindowsButton"; }
	}
	public static void main(String[] args) {
		Dialog dialog = new WindowsDialog();
		System.out.println(dialog.createButton());
	}
}

Parameterized Factory Method

You can parameterize a factory so it returns different object types based on an input value, such as a type code or name, rather than needing a separate factory method for every variant. Using an enum for that parameter, rather than a raw String or int, is strongly recommended since it catches invalid values at compile time.

Example: Parameterized Factory Method

java
public class Main {
	enum ShapeType { CIRCLE, SQUARE }
	static Object createShape(ShapeType type) {
		return switch (type) { // enum catches invalid values at compile time
			case CIRCLE -> "Circle";
			case SQUARE -> "Square";
		};
	}
	public static void main(String[] args) {
		System.out.println(createShape(ShapeType.SQUARE));
	}
}

Benefits of Decoupling

The Factory Pattern's core benefit is decoupling client code from the exact classes it depends on being instantiated. Clients are shielded from object-creation complexity entirely, whether that complexity is choosing between subclasses, reading configuration, or wiring up dependencies.

Example: Benefits of Decoupling

java
public class Main {
	interface Notifier { void send(String msg); }
	static class EmailNotifier implements Notifier {
		public void send(String msg) { System.out.println("Email: " + msg); }
	}
	static Notifier createNotifier() { return new EmailNotifier(); } // client shielded from creation details
	public static void main(String[] args) {
		Notifier n = createNotifier();
		n.send("Hello");
	}
}

Real-World Use Cases

Factories show up constantly in real systems: loading a database connection configured by a settings file, choosing a communication protocol implementation at runtime, or picking a specific notification channel (email, SMS, push) based on user preference are all natural fits for this pattern.

Example: Real-World Use Cases

java
public class Main {
	interface Notifier { void send(String msg); }
	static class SmsNotifier implements Notifier {
		public void send(String msg) { System.out.println("SMS: " + msg); }
	}
	static class EmailNotifier implements Notifier {
		public void send(String msg) { System.out.println("Email: " + msg); }
	}
	static Notifier getNotifier(String preference) {
		return preference.equals("sms") ? new SmsNotifier() : new EmailNotifier();
	}
	public static void main(String[] args) {
		getNotifier("sms").send("Order shipped");
	}
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.