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
EasyLet'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 calledShapethat serves as the common contract for all shapes. Your interface should declare a single methoddraw()that returns nothing but prints information about the shape being drawn.Shapes.java: Create three classes that implement yourShapeinterface:Circle- itsdraw()method should printDrawing a CircleRectangle- itsdraw()method should printDrawing a RectangleTriangle- itsdraw()method should printDrawing a TriangleShapeFactory.java: Create the factory class that handles shape creation. YourShapeFactoryshould have a static methodcreateShape(String type)that returns aShape. Based on the type parameter:- If type equals
"circle", return a newCircle - If type equals
"rectangle", return a newRectangle - If type equals
"triangle", return a newTriangle - For any other value, return
null
- If type equals
Main.java: Bring your factory system together! You'll receive two inputs: two shape types (both Strings).For each input, use your
ShapeFactoryto create the shape and store it in aShapevariable. If the factory returns a valid shape (not null), call itsdraw()method. If it returns null, printUnknown 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
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe this KeywordMethodsFields (Attributes)Constructor MethodConstructor OverloadingRecap - Simple Calculator4Inheritance
Basic Inheritance (extends)The super KeywordMethod Overriding (@Override)Constructor ChainingThe Object ClassSingle & Multilevel InheritWhy No Multi Class InheritRecap - Employee Hierarchy7Special Methods & Object Class
toString() Methodequals() and hashCode()clone() MethodcompareTo() and ComparableComparator InterfaceRecap - Custom Sorting2Access Modifiers & Encapsulate
Access Levels OverviewGetter and Setter MethodsInformation HidingThe final KeywordRecap - Bank Account Manager5Polymorphism
Method Overloading BasicsMethod Overriding (Run-Time)Upcasting and DowncastingThe instanceof OperatorAbstract Classes and MethodsRecap - Shape Calculator8Advanced OOP Concepts
Composition vs InheritanceAggregation vs CompositionInner Nested & Anonymous ClassEnums and Enum MethodsRecords (Java 16+)Sealed Classes (Java 17+)11Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternBuilder PatternObserver PatternStrategy Pattern3Class Props & Static Member
Instance vs Static VariablesStatic MethodsStatic BlocksConstants (static final)Recap - Counter & Utility6Interfaces & Abstract Classes
Introduction to InterfacesImplementing InterfacesMulti Interface ImplemenDefault & Static in InterfaceAbstract Classes vs InterfacesFunctional InterfacesRecap - Payment System