Master Creational Patterns for Efficient and Scalable Java Development
Explore how Singleton and Prototype patterns streamline Java applications by enhancing performance, reuse, and maintainability in real-world scenarios.
Design patterns are tried-and-tested solutions to common software design problems.
In Java, they help developers write clean, reusable, and scalable code.
This post focuses on two creational design patterns:
1. Singleton Design Pattern
2. Prototype Design Pattern
1. Singleton Design Pattern:
The Singleton pattern ensures a class has only one instance and provides a global point of access to it.
Why Use It:
Use Singleton when you want exactly one object to coordinate actions across the system. It’s perfect for things like:
Configuration settings
Database connections
Thread pools
Logging
Caches
Java Implementation (Thread-Safe Singleton):
public class ConfigManager {
private static volatile ConfigManager instance;
private ConfigManager() {
// private constructor
}
public static ConfigManager getInstance() {
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) {
instance = new ConfigManager();
}
}
}
return instance;
}
public void printConfig() {
System.out.println("App config data...");
}
}
Database Connection Manager:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnectionManager {
private static volatile DatabaseConnectionManager instance;
private Connection connection;
private static final String URL = "jdbc:mysql://localhost:3306/app_db";
private static final String USER = "root";
private static final String PASSWORD = "password";
private DatabaseConnectionManager() {
try {
this.connection = DriverManager.getConnection(URL, USER, PASSWORD);
} catch (SQLException e) {
throw new RuntimeException("Failed to connect to DB", e);
}
}
public static DatabaseConnectionManager getInstance() {
if (instance == null) {
synchronized (DatabaseConnectionManager.class) {
if (instance == null) {
instance = new DatabaseConnectionManager();
}
}
}
return instance;
}
public Connection getConnection() {
return connection;
}
}
public class UserService {
public void getUserData() {
try {
Connection conn = DatabaseConnectionManager.getInstance().getConnection();
// Use the connection for a query (using Statement, PreparedStatement, etc.)
} catch (Exception e) {
e.printStackTrace();
}
}
}
2. Prototype Design Pattern:
The Prototype pattern lets you clone existing objects instead of creating new ones from scratch. It’s especially useful when object creation is expensive (e.g., loading from DB, complex setup).
Why Use It:
When object construction is costly.
When you need many similar objects with slight variations.
When you want to avoid subclassing
Document Template System:
Building an enterprise app that generates business reports (invoices, summaries, charts). Each report starts from a base template, but the content is customized per user or client.
Instead of recreating everything from scratch, we clone a base report object and make changes.
public class ReportTemplate implements Cloneable {
private String title;
private String content;
private String footer;
public ReportTemplate(String title, String content, String footer) {
this.title = title;
this.content = content;
this.footer = footer;
}
public void setContent(String content) {
this.content = content;
}
public void print() {
System.out.println("=== " + title + " ===");
System.out.println(content);
System.out.println("--- " + footer + " ---");
}
@Override
public ReportTemplate clone() {
try {
return (ReportTemplate) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Failed to clone ReportTemplate", e);
}
}
}
public class Main {
public static void main(String[] args) {
// Base template
ReportTemplate monthlyReport = new ReportTemplate(
"Monthly Report",
"This is a placeholder content.",
"Confidential"
);
// Clone for Client A
ReportTemplate clientAReport = monthlyReport.clone();
clientAReport.setContent("Revenue: $10,000\nProfit: $2,500");
// Clone for Client B
ReportTemplate clientBReport = monthlyReport.clone();
clientBReport.setContent("Revenue: $12,000\nProfit: $3,000");
// Display both
clientAReport.print();
clientBReport.print();
}
}
When to Use What:
Use Case Singleton Prototype
One shared instance needed ✅ ❌
Performance matters in object creation ❌ ✅
Slight variations of the same object ❌ ✅
Global access point required ✅ ❌
Test yourself on what you've learned above, and compare your answers with the correct ones provided below.
Q1. What is the main purpose of the Singleton pattern?
A. Ensure multiple instances of a class
B. Allow cloning of objects
C. Ensure only one instance of a class
D. Avoid subclassing
Q2. Which of the following is a drawback of Singleton?
A. Easy to test
B. Promotes encapsulation
C. Global access causes tight coupling
D. Supports multiple threads easily
Q3. How do you prevent instantiation of a Singleton class from outside?
A. Declare the constructor public
B. Use static methods only
C. Declare the constructor private
D. Use `final` keyword
Q4. Which technique ensures thread-safe lazy initialization?
A. Simple static method
B. Early instantiation
C. Double-checked locking
D. Final keyword
Q5. What type of resource is best managed with a Singleton?
A. Immutable object
B. Large data list
C. Database connection
D. Temporary cache
Q6. What does the following code return?
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
System.out.println(obj1 == obj2);
A. true
B. false
C. Compilation error
D. Runtime error
Q7. Singleton is a type of:
A. Structural Pattern
B. Behavioral Pattern
C. Creational Pattern
D. Functional Pattern
Q8. Which of these ensures that a Singleton class cannot be subclassed?
A. abstract keyword
B. final keyword
C. private keyword
D. static keyword
Q9. Which is the best practice for Singleton in Java 8+?
A. Public constructor
B. Lazy instantiation without synchronization
C. Using Enum
D. Multiple static methods
Q10. In which case is Singleton not recommended?
A. Logging
B. Configuration loader
C. Thread pool manager
D. Frequently changed objects
Q11. What is the Prototype pattern primarily used for?
A. Avoiding thread safety
B. Deep copying data
C. Creating object copies
D. Creating subclasses
Q12. In Java, what interface must a class implement to support cloning?
A. Serializable
B. Comparable
C. Cloneable
D. Runnable
Q13. What does the `clone()` method do by default?
A. Creates a deep copy
B. Calls a constructor
C. Performs a shallow copy
D. Returns null
Q14. What exception must you handle when using `clone()`?
A. IOException
B. CloneNotSupportedException
C. NullPointerException
D. ClassNotFoundException
Q15. Which scenario is ideal for Prototype?
A. Shared object usage
B. Singleton implementation
C. Complex object creation
D. Real-time garbage collection
Q16. In a prototype pattern, object creation is handled through:
A. Constructors
B. Static methods
C. Cloning
D. Factories
Q17. What is a major disadvantage of using `Object.clone()` in Java?
A. It's deprecated
B. It can't clone any object
C. It’s protected and requires `Cloneable`
D. It uses too much memory
Q18. Which of the following is not a benefit of the Prototype pattern?
A. Reduces overhead of object creation
B. Promotes reuse of objects
C. Simplifies memory management
D. Enables run-time object configuration
Q19. How would you implement a deep clone manually in Java?
A. Use a shallow copy
B. Use reflection
C. Clone nested objects separately
D. Call `super.clone()` only
Q20. What kind of application would benefit most from Prototype?
A. Stateless REST APIs
B. Game engines spawning characters
C. Logging frameworks
D. String utilities
MCQ Keys:
Q1. Answer:C
Q2. Answer:C
Q3. Answer:C
Q4. Answer:C
Q5. Answer:C
Q6. Answer:A
Q7. Answer:C
Q8. Answer:B
Q9. Answer:C
Q10.Answer:D
Q9. Answer:C
Q10.Answer:D
Q11.Answer:C
Q12.Answer:C
Q13.Answer:C
Q14.Answer:B
Q15.Answer:C
Q16.Answer:C
Q17.Answer:C
Q18.Answer:C
Q19.Answer:C
Q20.Answer:B