Creational Design Patterns


18 Mar 2018  Sergio Martin Rubio  22 mins read.

Creational patterns allow you to create objects while hiding the creational logic, which increase flexibility and code reusability, so you decide which objects need to be created for a particular case.

All Patterns

Factory

classDiagram
class Connection {
	<<interface>>
	connect()
}

class MySQLConnection {
	
}
MySQLConnection: +connect()

class OracleConnection {
	
}
OracleConnection: +connect()

class MongoDBConnection {
	
}
MongoDBConnection: +connect()

class ConnectionFactory {
	
}
ConnectionFactory: +getConnection(String connectionType) Connection

class Client {
	
}

Connection <|.. MySQLConnection : implements
Connection <|.. OracleConnection : implements
Connection <|.. MongoDBConnection : implements
MySQLConnection <-- ConnectionFactory : creates
OracleConnection <-- ConnectionFactory : creates
MongoDBConnection <-- ConnectionFactory : creates
ConnectionFactory <-- Client : ask for new object

It allows you to create objects without exposing the creation logic to the client.

This pattern is very convenient when you have many objects of the same type and you manipulate them frequently.

Implementation

  1. Create an interface for the target objects:

     public interface Connection {
         void connect();
     }
    
  2. Create concrete classes implementing the interface previously created:

     public class MySQLConnection implements Connection {
         @Override
         public void connect() {
             System.out.println("Connecting to MySQL...");
         }
     }    
    

    Other implementations…

  3. A factory class is defined:

     public class ConnectionFactory {
    
         public Connection getConnection(String connectionType) {
             switch (connectionType) {
                 case "mysql":
                     return new MySQLConnection();
                 case "mongodb":
                     return new MongoDBConnection();
                 case "oracle":
                     return new OracleConnection();
                 default:
                     return null;
             }
         }
     }
    
  4. The factory class is used by the client to create a concrete class given a name:

     public class Client {
    
         public static void main(String[] args) {
    
             ConnectionFactory connectionFactory = new ConnectionFactory();
             Connection mysqlConnection = connectionFactory.getConnection("mysql");
    
             if (mysqlConnection != null) {
                 mysqlConnection.connect();
             } else {
                 System.out.println("Please, provide a valid DB type");
             }
         }
     }
    

Abstract Factory

This pattern is a factory of factories and it allows you to create related objects without explicitly specifying their classes.

This pattern is very convenient when you have many objects of the same family and you manipulate them frequently.

Implementation

  1. Create an interface for the target objects:

     public interface Connection {
         void connect();
     }
    
  2. Create concrete classes implementing the interface previously created:

     public class MySQLAWSConnection implements Connection {
         @Override
         public void connect() {
             System.out.println("Connecting to MySQL...");
         }
     }    
    

    …

  3. Create an interface to get factories of different family of objects:

     public interface ConnectionAbstractFactory {
         Connection getConnection(String connectionType);
     }
    
  4. Create factory classes extending the abstract factory class to generate object of concrete class given a name.

     public class AWSConnectionFactory implements ConnectionAbstractFactory {
         @Override
         public Connection getConnection(String connectionType) {
             switch (connectionType) {
                 case "mysql":
                     return new MySQLAWSConnection();
                 case "mongodb":
                     return new MongoDBAWSConnection();
                 case "oracle":
                     return new OracleAWSConnection();
                 default:
                     return null;
             }
         }
     }
    
     public class ConnectionFactory implements ConnectionAbstractFactory {
         @Override
         public Connection getConnection(String connectionType) {
             switch (connectionType) {
                 case "mysql":
                     return new MySQLConnection();
                 case "mongodb":
                     return new MongoDBConnection();
                 case "oracle":
                     return new OracleConnection();
                 default:
                     return null;
             }
         }
     }    
    
  5. Use the FactoryCreator to get the abstract factory class in order to get factories of concrete classes by passing names.

     public class Client {
    
         public static void main(String[] args) {
    
             ConnectionAbstractFactory awsConnectionFactory = FactoryCreator.getConnectionFactory(true);
             Connection mysqlAwsConnection = awsConnectionFactory.getConnection("mysql");
    
             if (mysqlAwsConnection != null) {
                 mysqlAwsConnection.connect();
             } else {
                 System.out.println("Please, provide a valid DB type");
             }
    
             ConnectionAbstractFactory regularConnectionFactory = FactoryCreator.getConnectionFactory(false);
             Connection mysqlRegularConnection = regularConnectionFactory.getConnection("mysql");
    
             if (mysqlRegularConnection != null) {
                 mysqlRegularConnection.connect();
             } else {
                 System.out.println("Please, provide a valid DB type");
             }
         }
     }
    

Singleton

It allows you to create a single instance of a class for the entire lifespan of your application, so you can make sure that a particular class is created only once.

It’s frequently used for logging classes, because a logging object usually needs to be used over and over again by many classes in the same application.

Singleton is a powerful pattern, but use it only when it is strictly necessary.

  • Singleton clients will be harder to test since it is not possible to substitute a mock implementation for a singleton class.
  • This pattern may behave as an anti-pattern, because it introduces global variables in your application.
  • Singletons are bad when implemented with multi-threading.

Implementation

classDiagram
class MySingleton{  
}

MySingleton : -MySingleton instance
MySingleton : -MySingleton()
MySingleton : +getInstance() MySingleton

MySingleton --> MySingleton

Singleton class implementations:

  • Eager initialization with private constructor and declaring a public final field
  • Eager initialization with private constructor and creating a static factory method
  • Lazy initialization and Double Check Locking pattern
  • Declare a single Enum field

Singleton With Immutable Public Field

  1. Create singleton class:

     public class MySingleton {
    
         public static final MySingleton instance = new MySingleton();
    
         private int value;
    
         private MySingleton() {
    
         }
    
         public void setValue(int value) {
             this.value = value;
         }
    
         public int getValue() {
             return value;
         }
     }
    
  2. The client can get the object from the singleton class:

     MySingleton instance = SingletonWithPublicFinalField.instance;
    
     instance.setValue(100);
     System.out.println(instance.getValue());
    

Considerations: The constructor can be invoked reflectively. Therefore, there are ways to create more than one instantiation of the class.

// reflection concept to get constructor of a Singleton class.
Constructor<MySingleton> constructor = MySingleton.class.getDeclaredConstructor();
// change the accessibility of constructor for outside a class object creation.
constructor.setAccessible(true);

// creates first object of a class as constructor is accessible now.
MySingleton firstSingleton = constructor.newInstance();
firstSingleton.setValue(5);

// creates second object of a class as constructor is accessible now.
MySingleton secondSingleton = constructor.newInstance();
secondSingleton.setValue(10);
System.out.println(secondSingleton.getValue()); // value = 10
System.out.println(firstSingleton.getValue()); // value = 5

constructor.setAccessible(false);

Singleton With Factory Method

  1. Create singleton class:

     public class MySingleton {
    
         private static MySingleton instance = new MySingleton();
    
         private int value;
    
         private MySingleton() {
    
         }
    
         public static MySingleton getInstance(){
             return instance;
         }
    
         public void setValue(int value) {
             this.value = value;
         }
    
         public int getValue() {
             return value;
         }
     }
    
  2. The client can get the object from the singleton class:

     MySingleton mySingleton = SingletonWithFactoryMethod.getInstance();
    
     mySingleton.setValue(200);
     System.out.println(mySingleton.getValue());
    

Advantages:

  • It is more flexible.
  • It allows you to create a generic singleton factory.
  • It is a supplier and allows you to call it as MySingleton::getInstance.

Disadvantages:

  • Constructor can be invoked reflectively.
  • If you are not going to take advantage of any of the previous points, it is recommended to use the first option.

Singleton With Lazy Initialization And Double Check Locking Pattern

This is similar to the previous singleton creation and it is only adding thread safety by using Double-Checked Locking Pattern.

  1. Create singleton class:

     public class MySingleton {
         private static volatile MySingleton instance;
    
         private MySingleton() {
    
         }
    
         public static MySingleton getInstance() {
             if (instance == null) { // 1st check
    
                 synchronized (MySingleton.class) {
    
                     if (instance == null) { // 2nd check
                         instance = new MySingleton();
                     }
                 }
             }
             return instance;
         }
     }
    
  2. The client can get the object from the singleton class:

     MySingleton mySingleton = MySingleton.getInstance();
    
     mySingleton.setValue(300);
     System.out.println(mySingleton.getValue());
    
Double-Checked Locking Pattern
  • A static volatile field is created to hold the instance. The variable is stored in the main memory, so reading or writing to the volatile variable will be on the main memory and not only on the CPU cache. This ensures that the singleton will never be half initialized.
  • The first check is not synchronized so that the common path — the instance already exists — costs nothing but a read, with no lock acquired.
  • The second check is needed because several threads can pass the first check while instance is still null; they then queue on the monitor one at a time, and only the first of them finds instance null and creates it.
  • Once initialization has happened no thread reaches the synchronized block again, so the lock is only ever contended during startup.
  • The volatile modifier is what makes this correct: without it the JVM is free to reorder the write that publishes the reference ahead of the constructor finishing, so another thread could observe a non-null but half-initialized instance.

Singleton With Enum

This is the preferred way.

  1. Create singleton class:

     public enum MySingleton {
    
         INSTANCE;
    
         private int value;
    
         public int getValue() {
             return value;
         }
    
         public void setValue(int value) {
             this.value = value;
         }
     }
    
  2. The client can get the object from the singleton enum:

     MySingleton mySingleton = MySingleton.INSTANCE;
    
     mySingleton.setValue(400);
     System.out.println(mySingleton.getValue());
    

Advantages:

  • Protection against reflection.
  • Provides Serialization. There is no way to create more than one instance of this type of singleton.
  • Thread-safe out-of-the-box.
  • More simple.

Disadvantages:

  • Your singleton cannot extend another class, since an enum already extends java.lang.Enum (it can still implement interfaces)

Builder

classDiagram
class Email {
	<<immutable>>
}
Email: -String from
Email: -String to
Email: -String subject
Email: -String body
Email: +builder(String from, String to) Builder

class Builder {
	
}
Builder: +subject(String subject) Builder
Builder: +body(String body) Builder
Builder: +cc(String address) Builder
Builder: +build() Email

class Client {
	
}

Email ..> Builder : declares as nested class
Client --> Builder : sets one field at a time
Builder --> Email : creates on build()

It lets you construct an object step by step, so the code that assembles the object is separated from the object itself.

This pattern solves the problem of a class with many constructor parameters, most of them optional. Without it you end up with telescoping constructors — a chain of overloads taking two, then three, then four arguments — which are painful to read at the call site, because new Email(from, to, null, null, subject) gives the reader no clue which argument is which. The alternative of a no-arg constructor plus setters is no better: it forces the class to be mutable and leaves the object in a half-built state in between.

A builder gives you named, chainable methods and lets the object stay immutable, since all the values are collected first and handed over in one go.

Keep in mind that a builder means writing and maintaining a second class that mirrors the fields of the first one. For a class with two or three required fields and nothing optional, a plain constructor is the better choice.

Implementation

  1. Make the target class immutable and give it a private constructor that takes the builder:

     public class Email {
    
         private final String from;
         private final String to;
         private final String subject;
         private final String body;
         private final List<String> cc;
    
         private Email(Builder builder) {
             this.from = builder.from;
             this.to = builder.to;
             this.subject = builder.subject;
             this.body = builder.body;
             this.cc = List.copyOf(builder.cc);
         }
    
         public static Builder builder(String from, String to) {
             return new Builder(from, to);
         }
    
         // getters...
     }
    
  2. Add the builder as a static nested class. Required values go in its constructor, optional ones get a method that returns the builder itself so calls can be chained:

     public static class Builder {
    
         private final String from;
         private final String to;
         private String subject = "";
         private String body = "";
         private final List<String> cc = new ArrayList<>();
    
         private Builder(String from, String to) {
             this.from = Objects.requireNonNull(from, "from is required");
             this.to = Objects.requireNonNull(to, "to is required");
         }
    
         public Builder subject(String subject) {
             this.subject = subject;
             return this;
         }
    
         public Builder body(String body) {
             this.body = body;
             return this;
         }
    
         public Builder cc(String address) {
             this.cc.add(address);
             return this;
         }
    
         public Email build() {
             return new Email(this);
         }
     }
    

    build() is the right place for any validation that involves more than one field, since it is the only point at which the whole object is known.

  3. The client reads as a description of what it is building:

     Email email = Email.builder("me@example.com", "you@example.com")
             .subject("Design patterns")
             .body("Have a look at the builder pattern")
             .cc("team@example.com")
             .build();
    

Advantages:

  • The call site is self-documenting, so you do not have to count arguments.
  • The built object can be immutable.
  • Optional fields cost nothing — you only mention the ones you want.

Disadvantages:

  • More code to maintain, and the builder has to be kept in sync with the fields of the target class.
  • The object is only validated at build() time rather than at compile time, so a missing required field is a runtime failure unless you put it in the builder’s constructor as above.

Prototype

classDiagram
class Document {
	
}
Document: -String title
Document: -List sections
Document: +clone() Document

class Client {
	
}

Client --> Document : asks an existing instance to copy itself
Document --> Document : clone() returns a new instance

It creates new objects by copying an existing instance instead of building one from scratch.

This is worth reaching for when creating an object is expensive — it needs a database round trip, parses a file, or does heavy computation — and you need many objects that only differ slightly. You pay the cost once to produce a fully initialized prototype, then copy it.

In Java the language-level support for this is Cloneable and Object.clone(), but be aware that it is a famously awkward API: Cloneable is a marker interface that declares no methods, clone() is protected on Object, and it throws a checked CloneNotSupportedException that you almost always have to swallow.

Because of that, a copy constructor or a static copy factory is usually the better option in Java — they are ordinary code with no checked exception, no cast and no reliance on super.clone(). Effective Java recommends them over Cloneable for exactly these reasons. Both are shown below.

Implementation

  1. The Cloneable approach. The important part is the shallow copy trap: super.clone() copies field values, so every reference field in the copy still points at the same object as the original. Any mutable field has to be copied explicitly:

     public class Document implements Cloneable {
    
         private String title;
         private List<String> sections;
    
         public Document(String title, List<String> sections) {
             this.title = title;
             this.sections = sections;
         }
    
         @Override
         public Document clone() {
             try {
                 Document copy = (Document) super.clone();
                 // without this line both documents would share one list
                 copy.sections = new ArrayList<>(this.sections);
                 return copy;
             } catch (CloneNotSupportedException e) {
                 throw new AssertionError("Document is Cloneable", e);
             }
         }
    
         public void addSection(String section) {
             this.sections.add(section);
         }
     }
    

    The difference is easy to demonstrate:

     Document original = new Document("Patterns", new ArrayList<>(List.of("Intro")));
     Document copy = original.clone();
    
     copy.addSection("Builder");
     // with the list copied:   original has 1 section, copy has 2
     // without it:            both have 2, because they share the same list
    

    String needs no special handling here because it is immutable — sharing it between the two objects is harmless. Only mutable reference fields have to be copied.

  2. The copy constructor, which is the idiomatic alternative and lets the class stay immutable:

     public class Document {
    
         private final String title;
         private final List<String> sections;
    
         public Document(String title, List<String> sections) {
             this.title = title;
             this.sections = List.copyOf(sections);
         }
    
         public Document(Document other) {
             this(other.title, other.sections);
         }
    
         public Document withTitle(String newTitle) {
             return new Document(newTitle, this.sections);
         }
     }
    
     Document original = new Document("Patterns", List.of("Intro"));
     Document renamed = original.withTitle("Creational Patterns");
    

Advantages:

  • Avoids repeating expensive initialization for every new object.
  • The client can copy an object without knowing its concrete class, when clone() is exposed through an interface.

Disadvantages:

  • Deciding how deep the copy should go is the hard part, and getting it wrong produces objects that quietly share state.
  • Cloneable bypasses constructors, so any invariant your constructor enforces is not applied to the copy — another reason to prefer a copy constructor.

Image by Foundry Co from Pixabay