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.
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
-
Create an interface for the target objects:
public interface Connection { void connect(); } -
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…
-
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; } } } -
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
-
Create an interface for the target objects:
public interface Connection { void connect(); } -
Create concrete classes implementing the interface previously created:
public class MySQLAWSConnection implements Connection { @Override public void connect() { System.out.println("Connecting to MySQL..."); } }…
-
Create an interface to get factories of different family of objects:
public interface ConnectionAbstractFactory { Connection getConnection(String connectionType); } -
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; } } } -
Use the
FactoryCreatorto 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
Enumfield
Singleton With Immutable Public Field
-
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; } } -
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
-
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; } } -
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.
-
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; } } -
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
instanceis still null; they then queue on the monitor one at a time, and only the first of them findsinstancenull and creates it. - Once initialization has happened no thread reaches the synchronized block again, so the lock is only ever contended during startup.
- The
volatilemodifier 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.
-
Create singleton class:
public enum MySingleton { INSTANCE; private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } } -
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
enumalready extendsjava.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
-
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... } -
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. -
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 overCloneablefor exactly these reasons. Both are shown below.
Implementation
-
The
Cloneableapproach. 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 listStringneeds no special handling here because it is immutable — sharing it between the two objects is harmless. Only mutable reference fields have to be copied. -
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.
Cloneablebypasses 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
