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

Java Factory Pattern

The factory pattern is a special maker that builds objects for you, like ordering a pizza without baking it yourself. You ask for a type, and it hands back the right one.
Syntax
java
interface Product { }

class Factory {
    static Product create(String type) {
        // return a new ConcreteProduct based on type
    }
}

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
// Define the class Main
public class Main {
	// Define the interface Notifier
	interface Notifier { void send(String msg); }
	// Define the class SmsNotifier
	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();
	}
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		getNotifier("sms").send("Order shipped");
	}
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Still writing new Circle() in the client code, which removes the whole point of the factory of hiding the concrete class.
  2. Making the factory method return the concrete class instead of the Shape interface, so callers depend on it anyway.
  3. Returning null for an unknown type in a parameterized factory, which causes a NullPointerException later; throw an IllegalArgumentException instead.

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.