Menu
Coddy logo textTech

Factory Pattern

Part of the Object Oriented Programming section of Coddy's Java journey — lesson 66 of 87.

The Factory Pattern is a creational design pattern that delegates object creation to a separate method or class. Instead of using new directly in your code, you ask a factory to create objects for you. This is especially useful when you have multiple related classes that share a common interface or parent.

Consider a notification system that can send emails, SMS, or push notifications. Without a factory, your code would need to know about every concrete class:

// Without Factory - client must know all concrete classes
Notification notification;
if (type.equals("email")) {
    notification = new EmailNotification();
} else if (type.equals("sms")) {
    notification = new SMSNotification();
}

With the Factory Pattern, you centralize this creation logic:

public interface Notification {
    void send(String message);
}

public class EmailNotification implements Notification {
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}

public class SMSNotification implements Notification {
    public void send(String message) {
        System.out.println("SMS: " + message);
    }
}

public class NotificationFactory {
    public static Notification create(String type) {
        if (type.equals("email")) {
            return new EmailNotification();
        } else if (type.equals("sms")) {
            return new SMSNotification();
        }
        return null;
    }
}

Now the client code is cleaner and doesn't depend on concrete classes:

Notification notification = NotificationFactory.create("email");
notification.send("Hello!");  // Output: Email: Hello!

The key benefit is that adding new notification types only requires updating the factory—client code remains unchanged. This follows the principle of programming to an interface, making your system more flexible and easier to extend.

challenge icon

Challenge

Easy

Let's build a shape drawing system using the Factory Pattern! Instead of creating shape objects directly throughout your code, you'll centralize the creation logic in a factory that produces different types of shapes based on a simple string identifier.

You'll organize your code across four files:

  • Shape.java: Define an interface called Shape that serves as the common contract for all shapes. Your interface should declare a single method draw() that returns nothing but prints information about the shape being drawn.
  • Shapes.java: Create three classes that implement your Shape interface:

    Circle - its draw() method should print Drawing a Circle

    Rectangle - its draw() method should print Drawing a Rectangle

    Triangle - its draw() method should print Drawing a Triangle

  • ShapeFactory.java: Create the factory class that handles shape creation. Your ShapeFactory should have a static method createShape(String type) that returns a Shape. Based on the type parameter:
    • If type equals "circle", return a new Circle
    • If type equals "rectangle", return a new Rectangle
    • If type equals "triangle", return a new Triangle
    • For any other value, return null
  • Main.java: Bring your factory system together! You'll receive two inputs: two shape types (both Strings).

    For each input, use your ShapeFactory to create the shape and store it in a Shape variable. If the factory returns a valid shape (not null), call its draw() method. If it returns null, print Unknown shape: [type] where [type] is the input that was provided.

    Process both inputs in order, each on its own line of output.

You will receive two inputs in order: the first shape type (String) and the second shape type (String).

Notice how your Main class never uses new Circle() or new Rectangle() directly—it only knows about the Shape interface and asks the factory to create objects. This is the power of the Factory Pattern: the client code is decoupled from the concrete classes!

Cheat sheet

The Factory Pattern is a creational design pattern that delegates object creation to a separate method or class, centralizing creation logic and decoupling client code from concrete implementations.

Instead of instantiating objects directly with new, you use a factory method:

// Without Factory - client depends on concrete classes
Notification notification;
if (type.equals("email")) {
    notification = new EmailNotification();
} else if (type.equals("sms")) {
    notification = new SMSNotification();
}

With the Factory Pattern, define a common interface and concrete implementations:

public interface Notification {
    void send(String message);
}

public class EmailNotification implements Notification {
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}

public class SMSNotification implements Notification {
    public void send(String message) {
        System.out.println("SMS: " + message);
    }
}

Create a factory class with a static creation method:

public class NotificationFactory {
    public static Notification create(String type) {
        if (type.equals("email")) {
            return new EmailNotification();
        } else if (type.equals("sms")) {
            return new SMSNotification();
        }
        return null;
    }
}

Client code uses the factory instead of direct instantiation:

Notification notification = NotificationFactory.create("email");
notification.send("Hello!");  // Output: Email: Hello!

Benefits: Adding new types only requires updating the factory—client code remains unchanged. This follows the principle of programming to an interface, making systems more flexible and easier to extend.

Try it yourself

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Read two shape types
        String type1 = scanner.nextLine();
        String type2 = scanner.nextLine();
        
        // TODO: Use ShapeFactory to create the first shape
        // If the shape is not null, call draw()
        // If the shape is null, print "Unknown shape: [type]"
        
        // TODO: Do the same for the second shape
        
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming