LLD
Machine Coding
Interview Course
Java โ€ข Interview Prep
Lesson 2 of 6

Factory Pattern


What is it? (The Analogy)

Walk into a bakery and say: "I'd like a chocolate cake." You don't walk into the kitchen, grab flour, eggs, and cocoa, preheat the oven, mix the batter, and bake it yourself. You just tell the bakery what you want, and the bakery creates it for you. You get back a delicious cake without knowing (or caring about) the 47 steps that went into making it.

The bakery is a factory. It takes your order (input), handles all the complex creation logic (the recipe), and returns a finished product (output). If the bakery adds a new "red velvet cake" to the menu, YOU don't need to learn anything new -- you just order it.

The Factory Pattern works the same way in code: instead of using new directly to create objects (which couples your code to specific classes), you ask a factory to create the object for you. The factory decides which concrete class to instantiate based on the input you provide.


Why do we need it?

Let's see the pain of creating objects directly:

java
1// BAD EXAMPLE -- Object creation scattered everywhere
2public class GameWorld {
3
4    public void spawnEnemy(String type, int x, int y) {
5        // Creation logic is hardcoded and repeated everywhere
6        if (type.equals("zombie")) {
7            Zombie z = new Zombie();
8            z.setHealth(100);
9            z.setSpeed(2);
10            z.setDamage(10);
11            z.setPosition(x, y);
12            z.setTexture("zombie_walk.png");
13            // ... 10 more setup lines
14        }
15        else if (type.equals("skeleton")) {
16            Skeleton s = new Skeleton();
17            s.setHealth(80);
18            s.setSpeed(4);
19            s.setDamage(15);
20            s.setBowRange(50);
21            s.setPosition(x, y);
22            s.setTexture("skeleton_idle.png");
23            // ... 10 more setup lines
24        }
25        else if (type.equals("dragon")) {
26            Dragon d = new Dragon();
27            d.setHealth(500);
28            d.setSpeed(8);
29            d.setFireDamage(50);
30            d.setWingspan(20);
31            d.setPosition(x, y);
32            d.setTexture("dragon_fly.png");
33            // ... 10 more setup lines
34        }
35        // This grows with every new enemy type...
36    }
37}

This same if-else block might be copy-pasted in GameWorld, LevelLoader, MultiplayerManager, and more. If you change how a Zombie is created, you need to find and update EVERY copy.

The pain: Object creation logic is scattered, duplicated, and tightly coupled to specific classes. Adding a new enemy means modifying every place enemies are created. Testing is hard because you can't mock the creation process.


How it works -- Step by Step

  1. Define a common interface or abstract class for the products (the objects being created).
  1. Create concrete product classes that implement the interface (Zombie, Skeleton, Dragon).
  1. Create a Factory class with a method that takes a parameter and returns the correct product.
  1. Move ALL creation logic into the factory -- constructors, setup, configuration, everything.
  1. Client code asks the factory for objects instead of using new directly. The client only knows about the interface, never the concrete classes.

Let's Build It Together

Let's build a Video Game Character Creator -- think of those screens where you pick a character class!

java
1// ========================================
2// Step 1: Define what every game character can do
3// ========================================
4
5public interface GameCharacter {
6    void attack();
7    void defend();
8    void specialAbility();
9    String getName();
10    int getHealth();
11}
java
1// ========================================
2// Step 2: Create concrete character classes
3// Each has its own unique stats and abilities
4// ========================================
5
6public class Warrior implements GameCharacter {
7    private String name;
8    private int health;
9    private int armor;
10
11    // Package-private constructor -- only the factory should call this!
12    Warrior(String name) {
13        this.name = name;
14        this.health = 150;   // Warriors are tanky
15        this.armor = 80;
16    }
17
18    @Override
19    public void attack() {
20        System.out.println(name + " swings a mighty sword! Deals 25 damage.");
21    }
22
23    @Override
24    public void defend() {
25        System.out.println(name + " raises a shield! Blocks " + armor + "% of damage.");
26    }
27
28    @Override
29    public void specialAbility() {
30        System.out.println(name + " uses BATTLE CRY! All allies gain +10 attack for 5 seconds.");
31    }
32
33    @Override
34    public String getName() { return name; }
35
36    @Override
37    public int getHealth() { return health; }
38}
39
40public class Mage implements GameCharacter {
41    private String name;
42    private int health;
43    private int mana;
44
45    Mage(String name) {
46        this.name = name;
47        this.health = 80;    // Mages are squishy
48        this.mana = 200;     // But lots of mana!
49    }
50
51    @Override
52    public void attack() {
53        System.out.println(name + " casts Fireball! Deals 40 magic damage.");
54    }
55
56    @Override
57    public void defend() {
58        System.out.println(name + " conjures a Magic Shield! Absorbs 50 damage.");
59    }
60
61    @Override
62    public void specialAbility() {
63        System.out.println(name + " uses METEOR STORM! Rains fire on all enemies for 60 damage!");
64    }
65
66    @Override
67    public String getName() { return name; }
68
69    @Override
70    public int getHealth() { return health; }
71}
72
73public class Archer implements GameCharacter {
74    private String name;
75    private int health;
76    private int range;
77
78    Archer(String name) {
79        this.name = name;
80        this.health = 100;
81        this.range = 50;     // Archers attack from far away
82    }
83
84    @Override
85    public void attack() {
86        System.out.println(name + " fires an arrow from " + range + " yards! Deals 30 damage.");
87    }
88
89    @Override
90    public void defend() {
91        System.out.println(name + " does a backflip to dodge! +50% evasion for 3 seconds.");
92    }
93
94    @Override
95    public void specialAbility() {
96        System.out.println(name + " uses RAIN OF ARROWS! Fires 20 arrows in an area!");
97    }
98
99    @Override
100    public String getName() { return name; }
101
102    @Override
103    public int getHealth() { return health; }
104}
105
106public class Healer implements GameCharacter {
107    private String name;
108    private int health;
109    private int healPower;
110
111    Healer(String name) {
112        this.name = name;
113        this.health = 90;
114        this.healPower = 40;
115    }
116
117    @Override
118    public void attack() {
119        System.out.println(name + " throws a holy light bolt! Deals 15 damage.");
120    }
121
122    @Override
123    public void defend() {
124        System.out.println(name + " casts Heal! Restores " + healPower + " HP.");
125    }
126
127    @Override
128    public void specialAbility() {
129        System.out.println(name + " uses DIVINE RESURRECTION! Revives a fallen ally!");
130    }
131
132    @Override
133    public String getName() { return name; }
134
135    @Override
136    public int getHealth() { return health; }
137}
java
1// ========================================
2// Step 3: The FACTORY -- centralizes ALL creation logic
3// ========================================
4
5public class CharacterFactory {
6
7    // An enum for type safety (better than raw strings!)
8    public enum CharacterClass {
9        WARRIOR, MAGE, ARCHER, HEALER
10    }
11
12    // The factory method -- takes a type and name, returns a ready-to-use character
13    public static GameCharacter createCharacter(CharacterClass type, String name) {
14        switch (type) {
15            case WARRIOR:
16                System.out.println("Forging armor for " + name + "...");
17                return new Warrior(name);
18
19            case MAGE:
20                System.out.println("Enchanting staff for " + name + "...");
21                return new Mage(name);
22
23            case ARCHER:
24                System.out.println("Crafting bow for " + name + "...");
25                return new Archer(name);
26
27            case HEALER:
28                System.out.println("Blessing robes for " + name + "...");
29                return new Healer(name);
30
31            default:
32                throw new IllegalArgumentException("Unknown character class: " + type);
33        }
34    }
35}
java
1// ========================================
2// Step 4: The game uses the factory -- never uses "new" directly!
3// ========================================
4
5public class GameApp {
6    public static void main(String[] args) {
7        System.out.println("=== CHARACTER SELECT ===\n");
8
9        // Create characters through the factory
10        GameCharacter player1 = CharacterFactory.createCharacter(
11            CharacterFactory.CharacterClass.WARRIOR, "Thorin"
12        );
13        GameCharacter player2 = CharacterFactory.createCharacter(
14            CharacterFactory.CharacterClass.MAGE, "Gandalf"
15        );
16        GameCharacter player3 = CharacterFactory.createCharacter(
17            CharacterFactory.CharacterClass.ARCHER, "Legolas"
18        );
19        GameCharacter player4 = CharacterFactory.createCharacter(
20            CharacterFactory.CharacterClass.HEALER, "Elrond"
21        );
22
23        System.out.println("\n=== BATTLE BEGINS ===\n");
24
25        // The game code only knows about GameCharacter interface
26        // It has NO idea about Warrior, Mage, Archer, Healer classes!
27        List<GameCharacter> party = List.of(player1, player2, player3, player4);
28
29        for (GameCharacter hero : party) {
30            System.out.println(hero.getName() + " (HP: " + hero.getHealth() + ")");
31            hero.attack();
32            hero.defend();
33            hero.specialAbility();
34            System.out.println();
35        }
36    }
37}

Aha moment: The GameApp class never imports or references Warrior, Mage, Archer, or Healer directly. It works entirely through GameCharacter and CharacterFactory. If you add a "Ninja" class tomorrow, only the factory needs a new case -- the game code stays identical!


Visual Mental Model

java
1  Client Code                    Factory                  Products
2  (GameApp)                   (CharacterFactory)          (Classes)
3  +-----------+               +------------------+
4  |           |  "WARRIOR"    |                  |------> Warrior
5  | "Give me  |  ----------> |  createCharacter |------> Mage
6  |  a MAGE"  |              |                  |------> Archer
7  |           |  <----------  |  (decides which  |------> Healer
8  | Gets back |  GameCharacter|   class to make) |------> ??? (future)
9  | interface |               +------------------+
10  +-----------+
11       |
12       v
13  Uses GameCharacter methods
14  (attack, defend, etc.)
15  without knowing the concrete class!

Common Mistakes & Gotchas

  • Exposing concrete classes alongside the factory -- If client code can still say new Warrior(), the factory is pointless. Consider making constructors package-private.
  • Putting too much logic in the factory -- The factory should CREATE objects, not contain business logic. Keep it focused on instantiation and initial setup.
  • Confusing Simple Factory with Factory Method and Abstract Factory -- Simple Factory (what we covered) is a class with a static/instance method. Factory Method uses inheritance (subclasses decide). Abstract Factory creates families of related objects. Start with Simple Factory!
  • Using strings instead of enums -- createCharacter("warrior") is error-prone. Use enums for type safety.
  • Not returning the interface -- If your factory method returns Warrior instead of GameCharacter, you lose the abstraction benefit. Always return the interface type.

Interview Tip

Factory Pattern is one of the first patterns interviewers expect you to mention. When designing a system, say: "Object creation should go through a factory so that the rest of the code is decoupled from specific classes." This is critical in problems like Parking Lot (different vehicle types), Chess (different piece types), Food Delivery (different restaurant types). Mention that it centralizes creation logic, making it easy to add new types and keeping client code clean.


Quick Quiz

  1. You're building a document editor that supports creating different document types: PDF, Word, and Spreadsheet. Each has different default formatting. Design a factory for this.
  1. What would happen if the CharacterFactory.createCharacter() method returned Warrior instead of GameCharacter? Why is this a problem?
  1. Your team has 5 different places in the codebase that create database connections, each with slightly different configuration. How could a factory help?

Summary -- Key Takeaways

  • Factory Pattern centralizes object creation in one place, so client code doesn't need to know which concrete class to instantiate.
  • Client code depends on interfaces, not implementations. The factory returns the interface type; the client never sees the concrete class.
  • Adding new types is easy -- add a new class and update the factory. No client code changes needed.
  • Use it when object creation is complex, involves setup/configuration, or when you have a family of related classes that should be chosen dynamically.