Design Patterns are proven, reusable solutions to commonly occurring problems in software design. They were formally cataloged in the 1994 book "Design Patterns: Elements of Reusable Object-Oriented Software" by the Gang of Four (GoF).
Creational Patterns deal with object creation mechanisms, trying to create objects in a manner suitable to the situation.
Intent: Ensure a class has only one instance, and provide a global point of access to it.
class DatabaseConnection {
// 1. Private static instance variable
private static DatabaseConnection instance;
// 2. Private constructor: Prevents external instantiation
private DatabaseConnection() {
System.out.println("Connecting to database...");
}
// 3. Public static method: The global access point
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
public void query(String sql) { /* execute query */ }
}
// Usage
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
// db1 and db2 point to the EXACT SAME object in memory.
The basic implementation above is NOT thread-safe. If two threads call getInstance() simultaneously when instance is null, both might create a new instance. Solutions include using synchronized, Double-Checked Locking, or the Bill Pugh Singleton (using a static inner class).
Intent: Define an interface for creating objects, but let subclasses decide which class to instantiate.
Without a factory, the client code is tightly coupled to concrete classes:
// BAD: Client code is tightly coupled to specific notification types
if (type.equals("email")) {
Notification n = new EmailNotification();
} else if (type.equals("sms")) {
Notification n = new SMSNotification();
} else if (type.equals("push")) {
Notification n = new PushNotification();
}
If you add a SlackNotification, you must find and modify this if-else chain everywhere in the codebase.
interface Notification {
void send(String message);
}
class EmailNotification implements Notification {
public void send(String msg) { System.out.println("Email: " + msg); }
}
class SMSNotification implements Notification {
public void send(String msg) { System.out.println("SMS: " + msg); }
}
// The Factory: Centralizes all creation logic
class NotificationFactory {
public static Notification create(String type) {
return switch (type) {
case "email" -> new EmailNotification();
case "sms" -> new SMSNotification();
default -> throw new IllegalArgumentException("Unknown type");
};
}
}
// Usage: Client code is decoupled from concrete classes
Notification n = NotificationFactory.create("email");
n.send("Hello!");
Now, adding a SlackNotification only requires modifying the single NotificationFactory class. All client code remains untouched.