Java Creational Patterns

by mahidhar
java-design-patternsjava-factory-patternsjava-singleton

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.