Design Patterns in Java: Creational Patterns
Design patterns are general reusable solutions to common problems in software design. Creational patterns deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. In this discussion, we’ll cover four creational patterns: Singleton, Factory, Builder, and Facade.
1. Singleton Pattern
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it.
Implementation
- Eager Initialization:
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
- Lazy Initialization:
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
- Thread-Safe Singleton:
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
- Double-Checked Locking:
public class DoubleCheckedLockingSingleton {
private static volatile DoubleCheckedLockingSingleton instance;
private DoubleCheckedLockingSingleton() {}
public static DoubleCheckedLockingSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedLockingSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedLockingSingleton();
}
}
}
return instance;
}
}
2. Factory Pattern
The Factory pattern provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.
Implementation
// Product interface
public interface Shape {
void draw();
}
// Concrete Products
public class Circle implements Shape {
public void draw() {
System.out.println("Drawing Circle");
}
}
public class Rectangle implements Shape {
public void draw() {
System.out.println("Drawing Rectangle");
}
}
// Factory Class
public class ShapeFactory {
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
}
return null;
}
}
// Client
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
// Get an object of Circle and call its draw method.
Shape shape1 = shapeFactory.getShape("CIRCLE");
shape1.draw();
// Get an object of Rectangle and call its draw method.
Shape shape2 = shapeFactory.getShape("RECTANGLE");
shape2.draw();
}
}
3. Builder Pattern
The Builder pattern is used to create complex objects step by step. It allows you to construct objects piece by piece.
Implementation
- Example:
// Product class
public class House {
private String foundation;
private String structure;
private String roof;
private String interior;
// Private constructor to enforce the use of the builder
private House(HouseBuilder builder) {
this.foundation = builder.foundation;
this.structure = builder.structure;
this.roof = builder.roof;
this.interior = builder.interior;
}
@Override
public String toString() {
return "House{" +
"foundation='" + foundation + '\'' +
", structure='" + structure + '\'' +
", roof='" + roof + '\'' +
", interior='" + interior + '\'' +
'}';
}
// Static inner Builder class
public static class HouseBuilder {
private String foundation;
private String structure;
private String roof;
private String interior;
public HouseBuilder setFoundation(String foundation) {
this.foundation = foundation;
return this;
}
public HouseBuilder setStructure(String structure) {
this.structure = structure;
return this;
}
public HouseBuilder setRoof(String roof) {
this.roof = roof;
return this;
}
public HouseBuilder setInterior(String interior) {
this.interior = interior;
return this;
}
public House build() {
return new House(this);
}
}
}
// Client
public class BuilderPatternDemo {
public static void main(String[] args) {
House house = new House.HouseBuilder()
.setFoundation("Concrete foundation")
.setStructure("Wooden structure")
.setRoof("Tile roof")
.setInterior("Modern interior")
.build();
System.out.println(house);
}
}
4. Facade Pattern
The Facade pattern provides a simplified interface to a complex subsystem. It hides the complexities of the system and provides an easy-to-use interface.
Implementation
- Example:
// Subsystem classes
public class CPU {
public void start() {
System.out.println("CPU started");
}
public void stop() {
System.out.println("CPU stopped");
}
}
public class Memory {
public void load(long position, byte[] data) {
System.out.println("Memory loaded at position " + position);
}
public void free(long position) {
System.out.println("Memory freed at position " + position);
}
}
public class HardDrive {
public byte[] read(long lba, int size) {
System.out.println("Hard drive read at lba " + lba + " with size " + size);
return new byte[size];
}
}
// Facade class
public class ComputerFacade {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public ComputerFacade() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
public void start() {
cpu.start();
memory.load(0, hardDrive.read(0, 4096));
System.out.println("Computer started");
}
public void stop() {
memory.free(0);
cpu.stop();
System.out.println("Computer stopped");
}
}
// Client
public class FacadePatternDemo {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.start();
computer.stop();
}
}
Summary
Creational design patterns provide solutions to instantiate objects in different ways to simplify system designs and make them more flexible and reusable.
- Singleton Pattern: Ensures a class has only one instance and provides a global point of access to it.
- Factory Pattern: Provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.
- Builder Pattern: Constructs complex objects step by step. The final step returns the object.
- Facade Pattern: Provides a simplified interface to a complex subsystem.
Understanding and implementing these patterns can significantly improve the design and maintainability of your code.