Java Creational Patterns

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:
Java
  public class EagerSingleton {
      private static final EagerSingleton INSTANCE = new EagerSingleton();

      private EagerSingleton() {}

      public static EagerSingleton getInstance() {
          return INSTANCE;
      }
  }
  • Lazy Initialization:
Java
  public class LazySingleton {
      private static LazySingleton instance;

      private LazySingleton() {}

      public static LazySingleton getInstance() {
          if (instance == null) {
              instance = new LazySingleton();
          }
          return instance;
      }
  }
  • Thread-Safe Singleton:
Java
  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:
Java
  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

    Java
      // 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:
    Java
      // 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:
    Java
      // 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.

    1. Singleton Pattern: Ensures a class has only one instance and provides a global point of access to it.
    2. Factory Pattern: Provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.
    3. Builder Pattern: Constructs complex objects step by step. The final step returns the object.
    4. 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.

    Scroll to Top