allthingsare🅿️.com Books ⬅️ Back allthingsare

2.1 Basic Design Patterns – Creational Patterns


Learning Goal:
• Learn core creational design patterns to solve repeated problems in software design.


Detailed Content:
• What are design patterns: reusable solutions for common problems
• Singleton pattern: concept, correct use cases, pitfalls
• Factory pattern: decoupling object creation
• Builder pattern: building complex objects step by step
• Simple code refactoring demo


2.1.1 What Are Design Patterns?

In software development, even the most experienced developers run into problems that look strikingly similar from one project to the next. Questions like “How should I create objects without tightly coupling my code?” or “How can I control how many instances of a class exist?” come up over and over again. This is where design patterns come in.

A design pattern is not a chunk of code you copy and paste; it is a proven, reusable solution for solving a specific type of problem in software design. Patterns capture the collective wisdom of many developers who have faced and solved similar challenges, and they provide a shared language that makes it easier for teams to discuss and implement robust designs.

Creational patterns in particular deal with one of the most fundamental questions in programming: how objects are created. Rather than leaving object creation scattered and inconsistent, creational patterns provide structured ways to handle instantiation, control the lifecycle of objects, and manage dependencies.


2.1.2 The Singleton Pattern

One of the simplest and most widely known creational patterns is the Singleton. The core idea behind the Singleton pattern is to ensure that a class has only one instance throughout the lifetime of an application, and to provide a single global access point to that instance.

This is useful in scenarios where exactly one object is needed to coordinate actions across a system. For example, a configuration manager that loads application settings only once and makes them available everywhere, or a logging service that collects logs from different parts of an app.

However, the Singleton pattern must be used with care. It can easily become a hidden form of global state, introducing tight coupling and making unit testing more difficult. A well-implemented Singleton is usually lazy-loaded (created only when needed) and thread-safe, especially in multi-threaded environments like web servers. Many frameworks, such as Spring, handle Singleton-like behavior for you through dependency injection, which avoids many pitfalls of manually coded Singletons.


2.1.3 The Factory Pattern

Another essential creational pattern is the Factory. The Factory pattern solves the problem of creating objects without specifying the exact class of the object that will be created.

Imagine you are building an application that supports multiple types of user notifications—email, SMS, push messages. If your code directly calls new EmailNotification() or new SMSNotification(), it becomes tightly bound to concrete classes. This makes it harder to extend or change the types of notifications later on.

The Factory pattern solves this by moving the creation logic into a separate Factory class or method. Your code calls the Factory and requests a Notification, passing in a parameter that tells the Factory which type to create. The Factory decides which concrete class to instantiate and returns an object that matches a common interface.

This decouples the object creation process from the main business logic. As a result, adding new notification types later does not require rewriting client code—only the Factory needs to know about the new type.


2.1.4 The Builder Pattern

The Builder pattern is a creational pattern that addresses another common problem: how to construct complex objects step by step without ending up with huge constructors full of optional parameters or messy code full of setter calls.

Suppose you want to create a User object that has required fields like name and email, but also optional fields like address, phone number, or profile picture. Writing a constructor with all possible combinations is impractical and unreadable.

The Builder pattern provides a neat solution. A separate Builder class or an inner Builder is used to configure each part of the object step by step. When all desired fields are set, a build() method constructs the final immutable object. This leads to clean, readable code and makes it easier to enforce validation rules during the building process.

Builders are common in frameworks and libraries. For example, many HTTP clients use Builders to construct requests with various headers, parameters, and payloads. Even in Java’s standard library, you can find Builders in classes like StringBuilder or StringBuffer, which build strings step by step.


2.1.5 Simple Code Refactoring Demo

To see how these patterns look in practice, consider a simple refactoring. Imagine you have a piece of code that directly creates different types of shapes:

java

Shape shape;
if (type.equals("circle")) { shape = new Circle(); } else if (type.equals("square")) { shape = new Square(); }

Over time, this if-else chain becomes a maintenance headache. Using a Factory, you can refactor it like this:

java

public class ShapeFactory {
public static Shape createShape(String type) { switch (type) { case "circle": return new Circle(); case "square": return new Square(); default: throw new IllegalArgumentException("Unknown shape type"); } } }

Now, instead of embedding object creation everywhere, your main code just calls:

java

Shape shape = ShapeFactory.createShape("circle");

This small change illustrates how creational patterns make systems easier to extend and maintain. Adding a new Triangle shape later requires no change to the main logic—only the Factory needs an extra case.

In real projects, these patterns—Singleton, Factory, and Builder—are often combined with other patterns and best practices to keep code clean, flexible, and scalable. Mastering them equips any developer to handle the unavoidable complexity that comes with real-world software.