Design patterns – Creational Patterns

In software engineering, a design pattern is a well-proven solution to a recurring problem in software design.
These patterns provide a structured approach to solving common architectural and design challenges, ensuring reusability, maintainability, and scalability. T
They provide standardized solutions that developers can reuse across various projects.

Why Use Design Patterns ?

Software development often involves solving complex problems. Without proper structuring, applications can become difficult to manage, extend, and scale. Design patterns help mitigate these issues by:

  • Providing reusable and proven solutions
  • Enhancing maintainability and reducing code duplication
  • Improving scalability and flexibility
  • Organizing code in a structured and readable manner
  • Encapsulating best practices

When to Consider Using a Design Pattern ?

You should consider applying a design pattern when:

  • You encounter a problem that a pattern is specifically designed to solve.
  • Scalability and maintainability are major concerns.
  • You are working on large applications or within a large development team where consistency is crucial.

Categories of Design Patterns

Design patterns are classified into three main categories:

Creational Patterns

Focus on object creation mechanisms, making instantiation more flexible and reusable.

Structural Patterns

Define how objects and classes interact to form larger structures.

Behavioral Patterns

Manage object interactions and responsibilities, improving communication between components.

Exploring Creational Design Patterns

In this article, we will explore Creational Design Patterns which focus on efficient and flexible object creation strategies

These patterns abstract complex instantiation processes, making the system more adaptable and easier to maintain.

Builder
Factory
Abstract Factory
Prototype
Singleton

Builder Pattern

The builder pattern is a design pattern that allows the creation of complex objects step by step while allowing immutability the construction process can change based on the type of the product being created

This method allows same construction process to create different representations by separating the construction process from the presentation of the object

Problem:
In OOP managing complex objects can be challenging due to many reason

  • The object has many optional parameters, leading to unreadable and error-prone constructor calls
  • constructor overloading with too many variations.
<span>public</span> <span>class</span> <span>House</span> <span>{</span>
<span>// If new parameters are added, all constructors must be updated //</span>
<span>private</span> <span>int</span> <span>walls</span><span>;</span>
<span>private</span> <span>int</span> <span>windows</span><span>;</span>
<span>private</span> <span>int</span> <span>doors</span><span>;</span>
<span>private</span> <span>String</span> <span>WallMaterial</span><span>;</span>
<span>private</span> <span>String</span> <span>woodType</span><span>;</span>
<span>private</span> <span>int</span> <span>hasPool</span><span>;</span>
<span>// To many constructors (Hard to maintain) //</span>
<span>public</span> <span>House</span><span>()</span> <span>{</span>
<span>}</span>
<span>// To many parameters hard to read // </span>
<span>public</span> <span>House</span><span>(</span><span>int</span> <span>walls</span><span>,</span> <span>int</span> <span>windows</span><span>,</span> <span>int</span> <span>doors</span><span>,</span> <span>String</span> <span>wallMaterial</span><span>,</span> <span>String</span> <span>woodType</span><span>,</span> <span>int</span> <span>hasPool</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>walls</span> <span>=</span> <span>walls</span><span>;</span>
<span>this</span><span>.</span><span>windows</span> <span>=</span> <span>windows</span><span>;</span>
<span>this</span><span>.</span><span>doors</span> <span>=</span> <span>doors</span><span>;</span>
<span>WallMaterial</span> <span>=</span> <span>wallMaterial</span><span>;</span>
<span>this</span><span>.</span><span>woodType</span> <span>=</span> <span>woodType</span><span>;</span>
<span>this</span><span>.</span><span>hasPool</span> <span>=</span> <span>hasPool</span><span>;</span>
<span>}</span>
<span>public</span> <span>House</span><span>(</span><span>int</span> <span>walls</span><span>,</span> <span>int</span> <span>windows</span><span>,</span> <span>int</span> <span>doors</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>walls</span> <span>=</span> <span>walls</span><span>;</span>
<span>this</span><span>.</span><span>windows</span> <span>=</span> <span>windows</span><span>;</span>
<span>this</span><span>.</span><span>doors</span> <span>=</span> <span>doors</span><span>;</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>House</span> <span>{</span>
    <span>// If new parameters are added, all constructors must be updated //</span>
    <span>private</span>  <span>int</span> <span>walls</span><span>;</span>
    <span>private</span>  <span>int</span> <span>windows</span><span>;</span>
    <span>private</span> <span>int</span> <span>doors</span><span>;</span>
    <span>private</span> <span>String</span> <span>WallMaterial</span><span>;</span>
    <span>private</span> <span>String</span> <span>woodType</span><span>;</span>
    <span>private</span> <span>int</span> <span>hasPool</span><span>;</span>



    <span>// To many constructors (Hard to maintain) //</span>
    <span>public</span> <span>House</span><span>()</span> <span>{</span>

    <span>}</span>
    <span>// To many parameters hard to read // </span>
    <span>public</span> <span>House</span><span>(</span><span>int</span> <span>walls</span><span>,</span> <span>int</span> <span>windows</span><span>,</span> <span>int</span> <span>doors</span><span>,</span> <span>String</span> <span>wallMaterial</span><span>,</span> <span>String</span> <span>woodType</span><span>,</span> <span>int</span> <span>hasPool</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>walls</span> <span>=</span> <span>walls</span><span>;</span>
        <span>this</span><span>.</span><span>windows</span> <span>=</span> <span>windows</span><span>;</span>
        <span>this</span><span>.</span><span>doors</span> <span>=</span> <span>doors</span><span>;</span>
        <span>WallMaterial</span> <span>=</span> <span>wallMaterial</span><span>;</span>
        <span>this</span><span>.</span><span>woodType</span> <span>=</span> <span>woodType</span><span>;</span>
        <span>this</span><span>.</span><span>hasPool</span> <span>=</span> <span>hasPool</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>House</span><span>(</span><span>int</span> <span>walls</span><span>,</span> <span>int</span> <span>windows</span><span>,</span> <span>int</span> <span>doors</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>walls</span> <span>=</span> <span>walls</span><span>;</span>
        <span>this</span><span>.</span><span>windows</span> <span>=</span> <span>windows</span><span>;</span>
        <span>this</span><span>.</span><span>doors</span> <span>=</span> <span>doors</span><span>;</span>
    <span>}</span>
<span>}</span>
public class House { // If new parameters are added, all constructors must be updated // private int walls; private int windows; private int doors; private String WallMaterial; private String woodType; private int hasPool; // To many constructors (Hard to maintain) // public House() { } // To many parameters hard to read // public House(int walls, int windows, int doors, String wallMaterial, String woodType, int hasPool) { this.walls = walls; this.windows = windows; this.doors = doors; WallMaterial = wallMaterial; this.woodType = woodType; this.hasPool = hasPool; } public House(int walls, int windows, int doors) { this.walls = walls; this.windows = windows; this.doors = doors; } }

Enter fullscreen mode Exit fullscreen mode

Solution:
The builder pattern solves this problem by
Encapsulating creation logic in a seperate class
Using Method chaining for readability
Ensuring that the final product immutable

implementation:

1.Define the Class (Product):

The Product class represents the object to be built. It contains multiple fields that define its properties. These fields are declared final, ensuring immutability. A private constructor enforces object creation through a builder.

<span>public</span> <span>class</span> <span>House</span> <span>{</span>
<span>private</span> <span>final</span> <span>int</span> <span>walls</span><span>;</span>
<span>private</span> <span>final</span> <span>int</span> <span>windows</span><span>;</span>
<span>private</span> <span>final</span> <span>int</span> <span>doors</span><span>;</span>
<span>private</span> <span>final</span> <span>String</span> <span>wallMaterial</span><span>;</span>
<span>private</span> <span>final</span> <span>String</span> <span>woodType</span><span>;</span>
<span>private</span> <span>final</span> <span>boolean</span> <span>hasPool</span><span>;</span>
<span>// Private constructor to enforce object creation through the Builder</span>
<span>House</span><span>(</span><span>int</span> <span>walls</span><span>,</span> <span>int</span> <span>windows</span><span>,</span> <span>int</span> <span>doors</span><span>,</span> <span>String</span> <span>wallMaterial</span><span>,</span> <span>String</span> <span>woodType</span><span>,</span> <span>boolean</span> <span>hasPool</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>walls</span> <span>=</span> <span>walls</span><span>;</span>
<span>this</span><span>.</span><span>windows</span> <span>=</span> <span>windows</span><span>;</span>
<span>this</span><span>.</span><span>doors</span> <span>=</span> <span>doors</span><span>;</span>
<span>this</span><span>.</span><span>wallMaterial</span> <span>=</span> <span>wallMaterial</span><span>;</span>
<span>this</span><span>.</span><span>woodType</span> <span>=</span> <span>woodType</span><span>;</span>
<span>this</span><span>.</span><span>hasPool</span> <span>=</span> <span>hasPool</span><span>;</span>
<span>}</span>
<span>// Getter methods for accessing private fields</span>
<span>public</span> <span>int</span> <span>getWalls</span><span>()</span> <span>{</span> <span>return</span> <span>walls</span><span>;</span> <span>}</span>
<span>public</span> <span>int</span> <span>getWindows</span><span>()</span> <span>{</span> <span>return</span> <span>windows</span><span>;</span> <span>}</span>
<span>public</span> <span>int</span> <span>getDoors</span><span>()</span> <span>{</span> <span>return</span> <span>doors</span><span>;</span> <span>}</span>
<span>public</span> <span>String</span> <span>getWallMaterial</span><span>()</span> <span>{</span> <span>return</span> <span>wallMaterial</span><span>;</span> <span>}</span>
<span>public</span> <span>String</span> <span>getWoodType</span><span>()</span> <span>{</span> <span>return</span> <span>woodType</span><span>;</span> <span>}</span>
<span>public</span> <span>boolean</span> <span>hasPool</span><span>()</span> <span>{</span> <span>return</span> <span>hasPool</span><span>;</span> <span>}</span>
<span>@Override</span>
<span>public</span> <span>String</span> <span>toString</span><span>()</span> <span>{</span>
<span>return</span> <span>"House{"</span> <span>+</span>
<span>"walls="</span> <span>+</span> <span>walls</span> <span>+</span>
<span>", windows="</span> <span>+</span> <span>windows</span> <span>+</span>
<span>", doors="</span> <span>+</span> <span>doors</span> <span>+</span>
<span>", wallMaterial='"</span> <span>+</span> <span>wallMaterial</span> <span>+</span> <span>'\''</span> <span>+</span>
<span>", woodType='"</span> <span>+</span> <span>woodType</span> <span>+</span> <span>'\''</span> <span>+</span>
<span>", hasPool="</span> <span>+</span> <span>hasPool</span> <span>+</span>
<span>'}'</span><span>;</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>House</span> <span>{</span>
    <span>private</span> <span>final</span> <span>int</span> <span>walls</span><span>;</span>
    <span>private</span> <span>final</span> <span>int</span> <span>windows</span><span>;</span>
    <span>private</span> <span>final</span> <span>int</span> <span>doors</span><span>;</span>
    <span>private</span> <span>final</span> <span>String</span> <span>wallMaterial</span><span>;</span>
    <span>private</span> <span>final</span> <span>String</span> <span>woodType</span><span>;</span>
    <span>private</span> <span>final</span> <span>boolean</span> <span>hasPool</span><span>;</span>

    <span>// Private constructor to enforce object creation through the Builder</span>

   <span>House</span><span>(</span><span>int</span> <span>walls</span><span>,</span> <span>int</span> <span>windows</span><span>,</span> <span>int</span> <span>doors</span><span>,</span> <span>String</span> <span>wallMaterial</span><span>,</span> <span>String</span> <span>woodType</span><span>,</span> <span>boolean</span> <span>hasPool</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>walls</span> <span>=</span> <span>walls</span><span>;</span>
        <span>this</span><span>.</span><span>windows</span> <span>=</span> <span>windows</span><span>;</span>
        <span>this</span><span>.</span><span>doors</span> <span>=</span> <span>doors</span><span>;</span>
        <span>this</span><span>.</span><span>wallMaterial</span> <span>=</span> <span>wallMaterial</span><span>;</span>
        <span>this</span><span>.</span><span>woodType</span> <span>=</span> <span>woodType</span><span>;</span>
        <span>this</span><span>.</span><span>hasPool</span> <span>=</span> <span>hasPool</span><span>;</span>
    <span>}</span>

    <span>// Getter methods for accessing private fields</span>
    <span>public</span> <span>int</span> <span>getWalls</span><span>()</span> <span>{</span> <span>return</span> <span>walls</span><span>;</span> <span>}</span>
    <span>public</span> <span>int</span> <span>getWindows</span><span>()</span> <span>{</span> <span>return</span> <span>windows</span><span>;</span> <span>}</span>
    <span>public</span> <span>int</span> <span>getDoors</span><span>()</span> <span>{</span> <span>return</span> <span>doors</span><span>;</span> <span>}</span>
    <span>public</span> <span>String</span> <span>getWallMaterial</span><span>()</span> <span>{</span> <span>return</span> <span>wallMaterial</span><span>;</span> <span>}</span>
    <span>public</span> <span>String</span> <span>getWoodType</span><span>()</span> <span>{</span> <span>return</span> <span>woodType</span><span>;</span> <span>}</span>
    <span>public</span> <span>boolean</span> <span>hasPool</span><span>()</span> <span>{</span> <span>return</span> <span>hasPool</span><span>;</span> <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>String</span> <span>toString</span><span>()</span> <span>{</span>
        <span>return</span> <span>"House{"</span> <span>+</span>
                <span>"walls="</span> <span>+</span> <span>walls</span> <span>+</span>
                <span>", windows="</span> <span>+</span> <span>windows</span> <span>+</span>
                <span>", doors="</span> <span>+</span> <span>doors</span> <span>+</span>
                <span>", wallMaterial='"</span> <span>+</span> <span>wallMaterial</span> <span>+</span> <span>'\''</span> <span>+</span>
                <span>", woodType='"</span> <span>+</span> <span>woodType</span> <span>+</span> <span>'\''</span> <span>+</span>
                <span>", hasPool="</span> <span>+</span> <span>hasPool</span> <span>+</span>
                <span>'}'</span><span>;</span>
    <span>}</span>
<span>}</span>
public class House { private final int walls; private final int windows; private final int doors; private final String wallMaterial; private final String woodType; private final boolean hasPool; // Private constructor to enforce object creation through the Builder House(int walls, int windows, int doors, String wallMaterial, String woodType, boolean hasPool) { this.walls = walls; this.windows = windows; this.doors = doors; this.wallMaterial = wallMaterial; this.woodType = woodType; this.hasPool = hasPool; } // Getter methods for accessing private fields public int getWalls() { return walls; } public int getWindows() { return windows; } public int getDoors() { return doors; } public String getWallMaterial() { return wallMaterial; } public String getWoodType() { return woodType; } public boolean hasPool() { return hasPool; } @Override public String toString() { return "House{" + "walls=" + walls + ", windows=" + windows + ", doors=" + doors + ", wallMaterial='" + wallMaterial + '\'' + ", woodType='" + woodType + '\'' + ", hasPool=" + hasPool + '}'; } }

Enter fullscreen mode Exit fullscreen mode

2.Define the Builder Interface:

The Builder interface defines the steps required to build a House object. It enforces consistency among different builders and ensures that each builder follows the same construction process.

<span>public</span> <span>interface</span> <span>Builder</span> <span>{</span>
<span>House</span> <span>build</span><span>();</span>
<span>void</span> <span>reset</span><span>();</span>
<span>}</span>
<span>public</span> <span>interface</span> <span>Builder</span> <span>{</span>
    <span>House</span> <span>build</span><span>();</span>
    <span>void</span> <span>reset</span><span>();</span> 
<span>}</span>
public interface Builder { House build(); void reset(); }

Enter fullscreen mode Exit fullscreen mode

3.Create the Concrete Builder Class:

The Concrete Builder class provides a step-by-step approach for constructing a House. It allows method chaining for a readable and elegant object creation process.

<span>public</span> <span>class</span> <span>HouseBuilder</span> <span>implements</span> <span>Builder</span> <span>{</span>
<span>private</span> <span>int</span> <span>walls</span><span>;</span>
<span>private</span> <span>int</span> <span>windows</span><span>;</span>
<span>private</span> <span>int</span> <span>doors</span><span>;</span>
<span>private</span> <span>String</span> <span>wallMaterial</span><span>;</span>
<span>private</span> <span>String</span> <span>woodType</span><span>;</span>
<span>private</span> <span>boolean</span> <span>hasPool</span><span>;</span>
<span>public</span> <span>HouseBuilder</span> <span>buildWalls</span><span>(</span><span>int</span> <span>walls</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>walls</span> <span>=</span> <span>walls</span><span>;</span>
<span>return</span> <span>this</span><span>;</span>
<span>}</span>
<span>public</span> <span>HouseBuilder</span> <span>buildWindows</span><span>(</span><span>int</span> <span>windows</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>windows</span> <span>=</span> <span>windows</span><span>;</span>
<span>return</span> <span>this</span><span>;</span>
<span>}</span>
<span>public</span> <span>HouseBuilder</span> <span>buildDoors</span><span>(</span><span>int</span> <span>doors</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>doors</span> <span>=</span> <span>doors</span><span>;</span>
<span>return</span> <span>this</span><span>;</span>
<span>}</span>
<span>public</span> <span>HouseBuilder</span> <span>buildWallMaterial</span><span>(</span><span>String</span> <span>material</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>wallMaterial</span> <span>=</span> <span>material</span><span>;</span>
<span>return</span> <span>this</span><span>;</span>
<span>}</span>
<span>public</span> <span>HouseBuilder</span> <span>buildWoodType</span><span>(</span><span>String</span> <span>woodType</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>woodType</span> <span>=</span> <span>woodType</span><span>;</span>
<span>return</span> <span>this</span><span>;</span>
<span>}</span>
<span>public</span> <span>HouseBuilder</span> <span>buildPool</span><span>(</span><span>boolean</span> <span>hasPool</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>hasPool</span> <span>=</span> <span>hasPool</span><span>;</span>
<span>return</span> <span>this</span><span>;</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>House</span> <span>build</span><span>()</span> <span>{</span>
<span>return</span> <span>new</span> <span>House</span><span>(</span><span>this</span><span>.</span><span>walls</span><span>,</span> <span>this</span><span>.</span><span>windows</span><span>,</span> <span>this</span><span>.</span><span>doors</span><span>,</span> <span>this</span><span>.</span><span>wallMaterial</span><span>,</span> <span>this</span><span>.</span><span>woodType</span><span>,</span> <span>this</span><span>.</span><span>hasPool</span><span>);</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>reset</span><span>()</span> <span>{</span>
<span>this</span><span>.</span><span>walls</span> <span>=</span> <span>0</span><span>;</span>
<span>this</span><span>.</span><span>windows</span> <span>=</span> <span>0</span><span>;</span>
<span>this</span><span>.</span><span>doors</span> <span>=</span> <span>0</span><span>;</span>
<span>this</span><span>.</span><span>wallMaterial</span> <span>=</span> <span>null</span><span>;</span>
<span>this</span><span>.</span><span>woodType</span> <span>=</span> <span>null</span><span>;</span>
<span>this</span><span>.</span><span>hasPool</span> <span>=</span> <span>false</span><span>;</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>HouseBuilder</span> <span>implements</span> <span>Builder</span> <span>{</span>
    <span>private</span> <span>int</span> <span>walls</span><span>;</span>
    <span>private</span> <span>int</span> <span>windows</span><span>;</span>
    <span>private</span> <span>int</span> <span>doors</span><span>;</span>
    <span>private</span> <span>String</span> <span>wallMaterial</span><span>;</span>
    <span>private</span> <span>String</span> <span>woodType</span><span>;</span>
    <span>private</span> <span>boolean</span> <span>hasPool</span><span>;</span>

    <span>public</span> <span>HouseBuilder</span> <span>buildWalls</span><span>(</span><span>int</span> <span>walls</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>walls</span> <span>=</span> <span>walls</span><span>;</span>
        <span>return</span> <span>this</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>HouseBuilder</span> <span>buildWindows</span><span>(</span><span>int</span> <span>windows</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>windows</span> <span>=</span> <span>windows</span><span>;</span>
        <span>return</span> <span>this</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>HouseBuilder</span> <span>buildDoors</span><span>(</span><span>int</span> <span>doors</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>doors</span> <span>=</span> <span>doors</span><span>;</span>
        <span>return</span> <span>this</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>HouseBuilder</span> <span>buildWallMaterial</span><span>(</span><span>String</span> <span>material</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>wallMaterial</span> <span>=</span> <span>material</span><span>;</span>
        <span>return</span> <span>this</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>HouseBuilder</span> <span>buildWoodType</span><span>(</span><span>String</span> <span>woodType</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>woodType</span> <span>=</span> <span>woodType</span><span>;</span>
        <span>return</span> <span>this</span><span>;</span>
    <span>}</span>

    <span>public</span> <span>HouseBuilder</span> <span>buildPool</span><span>(</span><span>boolean</span> <span>hasPool</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>hasPool</span> <span>=</span> <span>hasPool</span><span>;</span>
        <span>return</span> <span>this</span><span>;</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>House</span> <span>build</span><span>()</span> <span>{</span>
        <span>return</span> <span>new</span> <span>House</span><span>(</span><span>this</span><span>.</span><span>walls</span><span>,</span> <span>this</span><span>.</span><span>windows</span><span>,</span> <span>this</span><span>.</span><span>doors</span><span>,</span> <span>this</span><span>.</span><span>wallMaterial</span><span>,</span> <span>this</span><span>.</span><span>woodType</span><span>,</span> <span>this</span><span>.</span><span>hasPool</span><span>);</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>void</span> <span>reset</span><span>()</span> <span>{</span>
        <span>this</span><span>.</span><span>walls</span> <span>=</span> <span>0</span><span>;</span>
        <span>this</span><span>.</span><span>windows</span> <span>=</span> <span>0</span><span>;</span>
        <span>this</span><span>.</span><span>doors</span> <span>=</span> <span>0</span><span>;</span>
        <span>this</span><span>.</span><span>wallMaterial</span> <span>=</span> <span>null</span><span>;</span>
        <span>this</span><span>.</span><span>woodType</span> <span>=</span> <span>null</span><span>;</span>
        <span>this</span><span>.</span><span>hasPool</span> <span>=</span> <span>false</span><span>;</span>
    <span>}</span>
<span>}</span>
public class HouseBuilder implements Builder { private int walls; private int windows; private int doors; private String wallMaterial; private String woodType; private boolean hasPool; public HouseBuilder buildWalls(int walls) { this.walls = walls; return this; } public HouseBuilder buildWindows(int windows) { this.windows = windows; return this; } public HouseBuilder buildDoors(int doors) { this.doors = doors; return this; } public HouseBuilder buildWallMaterial(String material) { this.wallMaterial = material; return this; } public HouseBuilder buildWoodType(String woodType) { this.woodType = woodType; return this; } public HouseBuilder buildPool(boolean hasPool) { this.hasPool = hasPool; return this; } @Override public House build() { return new House(this.walls, this.windows, this.doors, this.wallMaterial, this.woodType, this.hasPool); } @Override public void reset() { this.walls = 0; this.windows = 0; this.doors = 0; this.wallMaterial = null; this.woodType = null; this.hasPool = false; } }

Enter fullscreen mode Exit fullscreen mode

4.Usage:

<span>public</span> <span>class</span> <span>Main</span> <span>{</span>
<span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
<span>House</span> <span>house</span> <span>=</span> <span>new</span> <span>HouseBuilder</span><span>()</span>
<span>.</span><span>buildWalls</span><span>(</span><span>4</span><span>)</span>
<span>.</span><span>buildWindows</span><span>(</span><span>8</span><span>)</span>
<span>.</span><span>buildDoors</span><span>(</span><span>2</span><span>)</span>
<span>.</span><span>buildWallMaterial</span><span>(</span><span>"Brick"</span><span>)</span>
<span>.</span><span>buildWoodType</span><span>(</span><span>"Oak"</span><span>)</span>
<span>.</span><span>buildPool</span><span>(</span><span>true</span><span>)</span>
<span>.</span><span>build</span><span>();</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"First House: "</span> <span>+</span> <span>house</span><span>.</span><span>toString</span><span>());</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>Main</span> <span>{</span>
    <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
        <span>House</span> <span>house</span> <span>=</span> <span>new</span> <span>HouseBuilder</span><span>()</span>
                <span>.</span><span>buildWalls</span><span>(</span><span>4</span><span>)</span>
                <span>.</span><span>buildWindows</span><span>(</span><span>8</span><span>)</span>
                <span>.</span><span>buildDoors</span><span>(</span><span>2</span><span>)</span>
                <span>.</span><span>buildWallMaterial</span><span>(</span><span>"Brick"</span><span>)</span>
                <span>.</span><span>buildWoodType</span><span>(</span><span>"Oak"</span><span>)</span>
                <span>.</span><span>buildPool</span><span>(</span><span>true</span><span>)</span>
                <span>.</span><span>build</span><span>();</span>

        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"First House: "</span> <span>+</span> <span>house</span><span>.</span><span>toString</span><span>());</span>
    <span>}</span>
<span>}</span>
public class Main { public static void main(String[] args) { House house = new HouseBuilder() .buildWalls(4) .buildWindows(8) .buildDoors(2) .buildWallMaterial("Brick") .buildWoodType("Oak") .buildPool(true) .build(); System.out.println("First House: " + house.toString()); } }

Enter fullscreen mode Exit fullscreen mode

The expected output of this code would be:

First House: House{walls=4, windows=8, doors=2, wallMaterial='Brick', woodType='Oak', hasPool=true}
First House: House{walls=4, windows=8, doors=2, wallMaterial='Brick', woodType='Oak', hasPool=true}
First House: House{walls=4, windows=8, doors=2, wallMaterial='Brick', woodType='Oak', hasPool=true}

Enter fullscreen mode Exit fullscreen mode

Advantages:
Immutable Objects
Step-by-Step Construction
Improved Readability and code Maintainability
Reduces Constructor Overloading

Limitations:
More Code & Complexity
Overhead for Simple Objects
Increased Memory Usage
Can Lead to Code Duplication

Conclusion:
The Builder Pattern is a powerful and flexible approach for creating complex objects. However, it adds complexity and is not always necessary for simple objects


Factory Pattern

The Factory Pattern provides an interface for creating objects without specifying their concrete classes allowing subclasses or implementing classes to determine which class to instantiate. The pattern delegates the responsibility of object creation to subclasses which implement the factory method to produce objects

  • It creates objects without exposing the instantiation logic
  • It refers to the created objects through a common interface
  • It decouples the client code from the classes being instantiated

Problem:

  • Tight coupling between business code and concrete classes making the systeme hard to modify
  • Violation of the Open/Closed Principle
  • Cases of complex object creation logic
  • Conditional instantiation complexity

Solution:
Reducing the high coupling by having the business code rely on the factory interface instead of the concrete classes
New product types can be created by adding new factory methods wich alling with the open/close principale (code should be open to extension and closed to modification)
Encapsulate complex creation logic (validations, configurations ..Etc) in the factory methods and keeps the business code clean and only focused on using the objects
Complex conditional creations are hidden in the factory implementation removing complex conditions from the business code and keeping it clean

1.Create the Product interface:
an interface that define a contract for all Product subclasses objects

<span>public</span> <span>interface</span> <span>PaymentGateway</span> <span>{</span>
<span>void</span> <span>transfer</span><span>();</span>
<span>void</span> <span>validate</span><span>();</span>
<span>}</span>
<span>public</span> <span>interface</span> <span>PaymentGateway</span> <span>{</span>
    <span>void</span> <span>transfer</span><span>();</span>
    <span>void</span> <span>validate</span><span>();</span>
<span>}</span>
public interface PaymentGateway { void transfer(); void validate(); }

Enter fullscreen mode Exit fullscreen mode

2.Create the Product subclasses implementations

<span>public</span> <span>class</span> <span>StripeGateway</span> <span>implements</span> <span>PaymentGateway</span> <span>{</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>transfer</span><span>()</span> <span>{</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Stripe gateway transfer"</span><span>);</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>validate</span><span>()</span> <span>{</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Stripe gateway validation"</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>PaypalGateway</span> <span>implements</span> <span>PaymentGateway</span> <span>{</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>transfer</span><span>()</span> <span>{</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Paypal gateway transfer"</span><span>);</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>validate</span><span>()</span> <span>{</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Paypal gateway validation"</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>StripeGateway</span> <span>implements</span> <span>PaymentGateway</span> <span>{</span>
    <span>@Override</span>
    <span>public</span> <span>void</span> <span>transfer</span><span>()</span> <span>{</span>
        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Stripe gateway transfer"</span><span>);</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>void</span> <span>validate</span><span>()</span> <span>{</span>
        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Stripe gateway validation"</span><span>);</span>
    <span>}</span>
<span>}</span>

<span>public</span> <span>class</span> <span>PaypalGateway</span> <span>implements</span> <span>PaymentGateway</span> <span>{</span>
    <span>@Override</span>
    <span>public</span> <span>void</span> <span>transfer</span><span>()</span> <span>{</span>
        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Paypal gateway transfer"</span><span>);</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>void</span> <span>validate</span><span>()</span> <span>{</span>
        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Paypal gateway validation"</span><span>);</span>
    <span>}</span>
<span>}</span>
public class StripeGateway implements PaymentGateway { @Override public void transfer() { System.out.println("Stripe gateway transfer"); } @Override public void validate() { System.out.println("Stripe gateway validation"); } } public class PaypalGateway implements PaymentGateway { @Override public void transfer() { System.out.println("Paypal gateway transfer"); } @Override public void validate() { System.out.println("Paypal gateway validation"); } }

Enter fullscreen mode Exit fullscreen mode

3.Create a factory class with static method create to return dynamicly the created objects

<span>public</span> <span>enum</span> <span>PaymentType</span> <span>{</span>
<span>STRIPE</span><span>,</span> <span>PAYPAL</span>
<span>}</span>
<span>public</span> <span>class</span> <span>PaymentGatewayFactory</span> <span>{</span>
<span>private</span> <span>static</span> <span>final</span> <span>Map</span><span><</span><span>PaymentType</span><span>,</span> <span>Supplier</span><span><</span><span>PaymentGateway</span><span>>></span> <span>gatewayMap</span> <span>=</span> <span>new</span> <span>HashMap</span><span><>();</span>
<span>// Registering available payment gateways</span>
<span>static</span> <span>{</span>
<span>gatewayMap</span><span>.</span><span>put</span><span>(</span><span>PaymentType</span><span>.</span><span>STRIPE</span><span>,</span> <span>StripeGateway:</span><span>:</span><span>new</span><span>);</span>
<span>gatewayMap</span><span>.</span><span>put</span><span>(</span><span>PaymentType</span><span>.</span><span>PAYPAL</span><span>,</span> <span>PaypalGateway:</span><span>:</span><span>new</span><span>);</span>
<span>}</span>
<span>// Factory method to create instances</span>
<span>public</span> <span>static</span> <span>PaymentGateway</span> <span>create</span><span>(</span><span>PaymentType</span> <span>type</span><span>)</span> <span>{</span>
<span>Supplier</span><span><</span><span>PaymentGateway</span><span>></span> <span>supplier</span> <span>=</span> <span>gatewayMap</span><span>.</span><span>get</span><span>(</span><span>type</span><span>);</span>
<span>if</span> <span>(</span><span>supplier</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span>
<span>return</span> <span>supplier</span><span>.</span><span>get</span><span>();</span>
<span>}</span>
<span>throw</span> <span>new</span> <span>IllegalArgumentException</span><span>(</span><span>type</span> <span>+</span> <span>" gateway is not supported"</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>enum</span> <span>PaymentType</span> <span>{</span>
    <span>STRIPE</span><span>,</span> <span>PAYPAL</span>
<span>}</span>


<span>public</span> <span>class</span> <span>PaymentGatewayFactory</span> <span>{</span>
    <span>private</span> <span>static</span> <span>final</span> <span>Map</span><span><</span><span>PaymentType</span><span>,</span> <span>Supplier</span><span><</span><span>PaymentGateway</span><span>>></span> <span>gatewayMap</span> <span>=</span> <span>new</span> <span>HashMap</span><span><>();</span>

    <span>// Registering available payment gateways</span>
    <span>static</span> <span>{</span>
        <span>gatewayMap</span><span>.</span><span>put</span><span>(</span><span>PaymentType</span><span>.</span><span>STRIPE</span><span>,</span> <span>StripeGateway:</span><span>:</span><span>new</span><span>);</span>
        <span>gatewayMap</span><span>.</span><span>put</span><span>(</span><span>PaymentType</span><span>.</span><span>PAYPAL</span><span>,</span> <span>PaypalGateway:</span><span>:</span><span>new</span><span>);</span>
    <span>}</span>

    <span>// Factory method to create instances</span>
    <span>public</span> <span>static</span> <span>PaymentGateway</span> <span>create</span><span>(</span><span>PaymentType</span> <span>type</span><span>)</span> <span>{</span>
        <span>Supplier</span><span><</span><span>PaymentGateway</span><span>></span> <span>supplier</span> <span>=</span> <span>gatewayMap</span><span>.</span><span>get</span><span>(</span><span>type</span><span>);</span>
        <span>if</span> <span>(</span><span>supplier</span> <span>!=</span> <span>null</span><span>)</span> <span>{</span>
            <span>return</span> <span>supplier</span><span>.</span><span>get</span><span>();</span>
        <span>}</span>
        <span>throw</span> <span>new</span> <span>IllegalArgumentException</span><span>(</span><span>type</span> <span>+</span> <span>" gateway is not supported"</span><span>);</span>
    <span>}</span>
<span>}</span>
public enum PaymentType { STRIPE, PAYPAL } public class PaymentGatewayFactory { private static final Map<PaymentType, Supplier<PaymentGateway>> gatewayMap = new HashMap<>(); // Registering available payment gateways static { gatewayMap.put(PaymentType.STRIPE, StripeGateway::new); gatewayMap.put(PaymentType.PAYPAL, PaypalGateway::new); } // Factory method to create instances public static PaymentGateway create(PaymentType type) { Supplier<PaymentGateway> supplier = gatewayMap.get(type); if (supplier != null) { return supplier.get(); } throw new IllegalArgumentException(type + " gateway is not supported"); } }

Enter fullscreen mode Exit fullscreen mode

4.Call the create method in business code to create instance of the desierd product

<span>public</span> <span>class</span> <span>Main</span> <span>{</span>
<span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
<span>// Create Stripe payment gateway</span>
<span>PaymentGateway</span> <span>stripe</span> <span>=</span> <span>PaymentGatewayFactory</span><span>.</span><span>create</span><span>(</span><span>PaymentType</span><span>.</span><span>STRIPE</span><span>);</span>
<span>stripe</span><span>.</span><span>validate</span><span>();</span>
<span>stripe</span><span>.</span><span>transfer</span><span>();</span>
<span>// Create Paypal payment gateway</span>
<span>PaymentGateway</span> <span>paypal</span> <span>=</span> <span>PaymentGatewayFactory</span><span>.</span><span>create</span><span>(</span><span>PaymentType</span><span>.</span><span>PAYPAL</span><span>);</span>
<span>paypal</span><span>.</span><span>validate</span><span>();</span>
<span>paypal</span><span>.</span><span>transfer</span><span>();</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>Main</span> <span>{</span>
    <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
        <span>// Create Stripe payment gateway</span>
        <span>PaymentGateway</span> <span>stripe</span> <span>=</span> <span>PaymentGatewayFactory</span><span>.</span><span>create</span><span>(</span><span>PaymentType</span><span>.</span><span>STRIPE</span><span>);</span>
        <span>stripe</span><span>.</span><span>validate</span><span>();</span>
        <span>stripe</span><span>.</span><span>transfer</span><span>();</span>

        <span>// Create Paypal payment gateway</span>
        <span>PaymentGateway</span> <span>paypal</span> <span>=</span> <span>PaymentGatewayFactory</span><span>.</span><span>create</span><span>(</span><span>PaymentType</span><span>.</span><span>PAYPAL</span><span>);</span>
        <span>paypal</span><span>.</span><span>validate</span><span>();</span>
        <span>paypal</span><span>.</span><span>transfer</span><span>();</span>
    <span>}</span>
<span>}</span>
public class Main { public static void main(String[] args) { // Create Stripe payment gateway PaymentGateway stripe = PaymentGatewayFactory.create(PaymentType.STRIPE); stripe.validate(); stripe.transfer(); // Create Paypal payment gateway PaymentGateway paypal = PaymentGatewayFactory.create(PaymentType.PAYPAL); paypal.validate(); paypal.transfer(); } }

Enter fullscreen mode Exit fullscreen mode

Advantges:
Encapsulation of Object Creation
Loose Coupling
Maintenance and Scalability
Code Reusability
Open-Closed Principle (OCP)

Limitations:
Increases Complexity
Can Violate Single Responsibility Principle

Conclusion:
The Factory Pattern simplifies object creation, promotes loose coupling, and enhances maintainability. However, it can add unnecessary complexity if overused. It’s best suited for scenarios where object instantiation logic is complex or frequently changing


Abstract Factory Pattern

The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. It encapsulates a group of individual factories that have a common theme, allowing client code to create objects from multiple related families without knowing their specific implementations

Problem:

  • Lack of Support for Families of Related Objects
  • risk of mixing incompatible objects
  • If the client needs multiple related objects we must create them separately leading to duplicated instantiation logic

Solution:
Encapsulates Object Families by providing a single interface for creating multiple related objects

1.Define Abstract Product Interfaces

<span>public</span> <span>interface</span> <span>Button</span> <span>{</span>
<span>void</span> <span>render</span><span>();</span>
<span>}</span>
<span>public</span> <span>interface</span> <span>Checkbox</span> <span>{</span>
<span>void</span> <span>render</span><span>();</span>
<span>}</span>
<span>public</span> <span>interface</span> <span>Button</span> <span>{</span>
    <span>void</span> <span>render</span><span>();</span>
<span>}</span>

<span>public</span> <span>interface</span> <span>Checkbox</span> <span>{</span>
    <span>void</span> <span>render</span><span>();</span>
<span>}</span>
public interface Button { void render(); } public interface Checkbox { void render(); }

Enter fullscreen mode Exit fullscreen mode

2.Create the Concrete Product Implementations

<span>public</span> <span>class</span> <span>DarkButton</span> <span>implements</span> <span>Button</span> <span>{</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>render</span><span>()</span> <span>{</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Rendering Dark Theme Button"</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>LightButton</span> <span>implements</span> <span>Button</span> <span>{</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>render</span><span>()</span> <span>{</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Rendering Light Theme Button"</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>DarkCheckbox</span> <span>implements</span> <span>Checkbox</span> <span>{</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>render</span><span>()</span> <span>{</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Rendering Dark Theme Checkbox"</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>LightCheckbox</span> <span>implements</span> <span>Checkbox</span> <span>{</span>
<span>@Override</span>
<span>public</span> <span>void</span> <span>render</span><span>()</span> <span>{</span>
<span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Rendering Light Theme Checkbox"</span><span>);</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>DarkButton</span> <span>implements</span> <span>Button</span> <span>{</span>
    <span>@Override</span>
    <span>public</span> <span>void</span> <span>render</span><span>()</span> <span>{</span>
        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Rendering Dark Theme Button"</span><span>);</span>
    <span>}</span>
<span>}</span>

<span>public</span> <span>class</span> <span>LightButton</span> <span>implements</span> <span>Button</span> <span>{</span>
    <span>@Override</span>
    <span>public</span> <span>void</span> <span>render</span><span>()</span> <span>{</span>
        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Rendering Light Theme Button"</span><span>);</span>
    <span>}</span>
<span>}</span>

<span>public</span> <span>class</span> <span>DarkCheckbox</span> <span>implements</span> <span>Checkbox</span> <span>{</span>
    <span>@Override</span>
    <span>public</span> <span>void</span> <span>render</span><span>()</span> <span>{</span>
        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Rendering Dark Theme Checkbox"</span><span>);</span>
    <span>}</span>
<span>}</span>

<span>public</span> <span>class</span> <span>LightCheckbox</span> <span>implements</span> <span>Checkbox</span> <span>{</span>
    <span>@Override</span>
    <span>public</span> <span>void</span> <span>render</span><span>()</span> <span>{</span>
        <span>System</span><span>.</span><span>out</span><span>.</span><span>println</span><span>(</span><span>"Rendering Light Theme Checkbox"</span><span>);</span>
    <span>}</span>
<span>}</span>
public class DarkButton implements Button { @Override public void render() { System.out.println("Rendering Dark Theme Button"); } } public class LightButton implements Button { @Override public void render() { System.out.println("Rendering Light Theme Button"); } } public class DarkCheckbox implements Checkbox { @Override public void render() { System.out.println("Rendering Dark Theme Checkbox"); } } public class LightCheckbox implements Checkbox { @Override public void render() { System.out.println("Rendering Light Theme Checkbox"); } }

Enter fullscreen mode Exit fullscreen mode

3.Define the Abstract Factory Interface

<span>public</span> <span>interface</span> <span>UiFactory</span> <span>{</span>
<span>Button</span> <span>createButton</span><span>();</span>
<span>Checkbox</span> <span>createCheckbox</span><span>();</span>
<span>}</span>
<span>public</span> <span>interface</span> <span>UiFactory</span> <span>{</span>
    <span>Button</span> <span>createButton</span><span>();</span>
    <span>Checkbox</span> <span>createCheckbox</span><span>();</span>
<span>}</span>
public interface UiFactory { Button createButton(); Checkbox createCheckbox(); }

Enter fullscreen mode Exit fullscreen mode

4.Define the concrete factories implementations

<span>public</span> <span>class</span> <span>DarkThemeFactory</span> <span>implements</span> <span>UiFactory</span> <span>{</span>
<span>// Singleton instance <Check singleton pattern></span>
<span>private</span> <span>static</span> <span>final</span> <span>DarkThemeFactory</span> <span>INSTANCE</span> <span>=</span> <span>new</span> <span>DarkThemeFactory</span><span>();</span>
<span>private</span> <span>DarkThemeFactory</span><span>()</span> <span>{}</span>
<span>public</span> <span>static</span> <span>DarkThemeFactory</span> <span>getInstance</span><span>()</span> <span>{</span>
<span>return</span> <span>INSTANCE</span><span>;</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>Button</span> <span>createButton</span><span>()</span> <span>{</span>
<span>return</span> <span>new</span> <span>DarkButton</span><span>();</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>Checkbox</span> <span>createCheckbox</span><span>()</span> <span>{</span>
<span>return</span> <span>new</span> <span>DarkCheckbox</span><span>();</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>LightThemeFactory</span> <span>implements</span> <span>UiFactory</span> <span>{</span>
<span>private</span> <span>static</span> <span>final</span> <span>LightThemeFactory</span> <span>INSTANCE</span> <span>=</span> <span>new</span> <span>LightThemeFactory</span><span>();</span>
<span>private</span> <span>LightThemeFactory</span><span>()</span> <span>{}</span>
<span>public</span> <span>static</span> <span>LightThemeFactory</span> <span>getInstance</span><span>()</span> <span>{</span>
<span>return</span> <span>INSTANCE</span><span>;</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>Button</span> <span>createButton</span><span>()</span> <span>{</span>
<span>return</span> <span>new</span> <span>LightButton</span><span>();</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>Checkbox</span> <span>createCheckbox</span><span>()</span> <span>{</span>
<span>return</span> <span>new</span> <span>LightCheckbox</span><span>();</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>DarkThemeFactory</span> <span>implements</span> <span>UiFactory</span> <span>{</span>
    <span>// Singleton instance <Check singleton pattern></span>
    <span>private</span> <span>static</span> <span>final</span> <span>DarkThemeFactory</span> <span>INSTANCE</span> <span>=</span> <span>new</span> <span>DarkThemeFactory</span><span>();</span>
    <span>private</span> <span>DarkThemeFactory</span><span>()</span> <span>{}</span> 

    <span>public</span> <span>static</span> <span>DarkThemeFactory</span> <span>getInstance</span><span>()</span> <span>{</span>
        <span>return</span> <span>INSTANCE</span><span>;</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>Button</span> <span>createButton</span><span>()</span> <span>{</span>
        <span>return</span> <span>new</span> <span>DarkButton</span><span>();</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>Checkbox</span> <span>createCheckbox</span><span>()</span> <span>{</span>
        <span>return</span> <span>new</span> <span>DarkCheckbox</span><span>();</span>
    <span>}</span>
<span>}</span>

<span>public</span> <span>class</span> <span>LightThemeFactory</span> <span>implements</span> <span>UiFactory</span> <span>{</span>
    <span>private</span> <span>static</span> <span>final</span> <span>LightThemeFactory</span> <span>INSTANCE</span> <span>=</span> <span>new</span> <span>LightThemeFactory</span><span>();</span> 

    <span>private</span> <span>LightThemeFactory</span><span>()</span> <span>{}</span>

    <span>public</span> <span>static</span> <span>LightThemeFactory</span> <span>getInstance</span><span>()</span> <span>{</span>
        <span>return</span> <span>INSTANCE</span><span>;</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>Button</span> <span>createButton</span><span>()</span> <span>{</span>
        <span>return</span> <span>new</span> <span>LightButton</span><span>();</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>Checkbox</span> <span>createCheckbox</span><span>()</span> <span>{</span>
        <span>return</span> <span>new</span> <span>LightCheckbox</span><span>();</span>
    <span>}</span>
<span>}</span>
public class DarkThemeFactory implements UiFactory { // Singleton instance <Check singleton pattern> private static final DarkThemeFactory INSTANCE = new DarkThemeFactory(); private DarkThemeFactory() {} public static DarkThemeFactory getInstance() { return INSTANCE; } @Override public Button createButton() { return new DarkButton(); } @Override public Checkbox createCheckbox() { return new DarkCheckbox(); } } public class LightThemeFactory implements UiFactory { private static final LightThemeFactory INSTANCE = new LightThemeFactory(); private LightThemeFactory() {} public static LightThemeFactory getInstance() { return INSTANCE; } @Override public Button createButton() { return new LightButton(); } @Override public Checkbox createCheckbox() { return new LightCheckbox(); } }

Enter fullscreen mode Exit fullscreen mode

5.Define the factory provider

<span>public</span> <span>enum</span> <span>ThemeType</span> <span>{</span>
<span>DARK</span><span>,</span> <span>LIGHT</span>
<span>}</span>
<span>public</span> <span>class</span> <span>FactoryProvider</span> <span>{</span>
<span>public</span> <span>static</span> <span>UiFactory</span> <span>getFactory</span><span>(</span><span>ThemeType</span> <span>theme</span><span>)</span> <span>{</span>
<span>switch</span> <span>(</span><span>theme</span><span>)</span> <span>{</span>
<span>case</span> <span>DARK:</span>
<span>return</span> <span>DarkThemeFactory</span><span>.</span><span>getInstance</span><span>();</span>
<span>case</span> <span>LIGHT:</span>
<span>return</span> <span>LightThemeFactory</span><span>.</span><span>getInstance</span><span>();</span>
<span>default</span><span>:</span>
<span>throw</span> <span>new</span> <span>IllegalArgumentException</span><span>(</span><span>"Unknown theme: "</span> <span>+</span> <span>theme</span><span>);</span>
<span>}</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>enum</span> <span>ThemeType</span> <span>{</span> 
<span>DARK</span><span>,</span> <span>LIGHT</span>
<span>}</span>

<span>public</span> <span>class</span> <span>FactoryProvider</span> <span>{</span>
    <span>public</span> <span>static</span> <span>UiFactory</span> <span>getFactory</span><span>(</span><span>ThemeType</span> <span>theme</span><span>)</span> <span>{</span>
        <span>switch</span> <span>(</span><span>theme</span><span>)</span> <span>{</span>
            <span>case</span> <span>DARK:</span>
                <span>return</span> <span>DarkThemeFactory</span><span>.</span><span>getInstance</span><span>();</span>
            <span>case</span> <span>LIGHT:</span>
                <span>return</span> <span>LightThemeFactory</span><span>.</span><span>getInstance</span><span>();</span>
            <span>default</span><span>:</span>
                <span>throw</span> <span>new</span> <span>IllegalArgumentException</span><span>(</span><span>"Unknown theme: "</span> <span>+</span> <span>theme</span><span>);</span>
        <span>}</span>
    <span>}</span>
<span>}</span>
public enum ThemeType { DARK, LIGHT } public class FactoryProvider { public static UiFactory getFactory(ThemeType theme) { switch (theme) { case DARK: return DarkThemeFactory.getInstance(); case LIGHT: return LightThemeFactory.getInstance(); default: throw new IllegalArgumentException("Unknown theme: " + theme); } } }

Enter fullscreen mode Exit fullscreen mode

6.Usage

<span>public</span> <span>class</span> <span>Main</span> <span>{</span>
<span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
<span>UiFactory</span> <span>darkFactory</span> <span>=</span> <span>FactoryProvider</span><span>.</span><span>getFactory</span><span>(</span><span>ThemeType</span><span>.</span><span>DARK</span><span>);</span>
<span>Button</span> <span>darkButton</span> <span>=</span> <span>darkFactory</span><span>.</span><span>createButton</span><span>();</span>
<span>Checkbox</span> <span>darkCheckbox</span> <span>=</span> <span>darkFactory</span><span>.</span><span>createCheckbox</span><span>();</span>
<span>darkButton</span><span>.</span><span>render</span><span>();</span>
<span>darkCheckbox</span><span>.</span><span>render</span><span>();</span>
<span>UiFactory</span> <span>lightFactory</span> <span>=</span> <span>FactoryProvider</span><span>.</span><span>getFactory</span><span>(</span><span>ThemeType</span><span>.</span><span>LIGHT</span><span>);</span>
<span>Button</span> <span>lightButton</span> <span>=</span> <span>lightFactory</span><span>.</span><span>createButton</span><span>();</span>
<span>Checkbox</span> <span>lightCheckbox</span> <span>=</span> <span>lightFactory</span><span>.</span><span>createCheckbox</span><span>();</span>
<span>lightButton</span><span>.</span><span>render</span><span>();</span>
<span>lightCheckbox</span><span>.</span><span>render</span><span>();</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>Main</span> <span>{</span>
    <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
        <span>UiFactory</span> <span>darkFactory</span> <span>=</span> <span>FactoryProvider</span><span>.</span><span>getFactory</span><span>(</span><span>ThemeType</span><span>.</span><span>DARK</span><span>);</span>
        <span>Button</span> <span>darkButton</span> <span>=</span> <span>darkFactory</span><span>.</span><span>createButton</span><span>();</span>
        <span>Checkbox</span> <span>darkCheckbox</span> <span>=</span> <span>darkFactory</span><span>.</span><span>createCheckbox</span><span>();</span>

        <span>darkButton</span><span>.</span><span>render</span><span>();</span>
        <span>darkCheckbox</span><span>.</span><span>render</span><span>();</span>

        <span>UiFactory</span> <span>lightFactory</span> <span>=</span> <span>FactoryProvider</span><span>.</span><span>getFactory</span><span>(</span><span>ThemeType</span><span>.</span><span>LIGHT</span><span>);</span>
        <span>Button</span> <span>lightButton</span> <span>=</span> <span>lightFactory</span><span>.</span><span>createButton</span><span>();</span>
        <span>Checkbox</span> <span>lightCheckbox</span> <span>=</span> <span>lightFactory</span><span>.</span><span>createCheckbox</span><span>();</span>

        <span>lightButton</span><span>.</span><span>render</span><span>();</span>
        <span>lightCheckbox</span><span>.</span><span>render</span><span>();</span>
    <span>}</span>
<span>}</span>
public class Main { public static void main(String[] args) { UiFactory darkFactory = FactoryProvider.getFactory(ThemeType.DARK); Button darkButton = darkFactory.createButton(); Checkbox darkCheckbox = darkFactory.createCheckbox(); darkButton.render(); darkCheckbox.render(); UiFactory lightFactory = FactoryProvider.getFactory(ThemeType.LIGHT); Button lightButton = lightFactory.createButton(); Checkbox lightCheckbox = lightFactory.createCheckbox(); lightButton.render(); lightCheckbox.render(); } }

Enter fullscreen mode Exit fullscreen mode

Advantages:
Consistency Across Related Objects
Loose Coupling
Maintainability

Limitations:
Increased Complexity
Difficult to Extend

Conclusion:
The Abstract Factory Pattern ensures consistency across families of related objects while promoting loose coupling and maintainability. However, it adds complexity and makes adding new product types harder. It’s best used when a system requires multiple related objects that must work together


Prototype Pattern

The prototype pattern or also known as Clone pattern allows to object duplication by cloning an existing object instead of creating a new instance from scratch. This pattern is useful when object creation is costly or complex, and cloning provides a more efficient alternative

Problem:

  • Expensive Object Creation
  • Complex Object Configuration
  • Too many constructors or factory methods make maintenance difficult

Solution:
Clone an existing object instead of recreating it
Cloning allows flexible object variations without subclassing

1.Define the Prototype interface with a clone() method

<span>public</span> <span>interface</span> <span>Prototype</span><span><</span><span>T</span><span>></span> <span>{</span>
<span>T</span> <span>clone</span><span>();</span>
<span>}</span>
<span>public</span> <span>interface</span> <span>Prototype</span><span><</span><span>T</span><span>></span> <span>{</span>
    <span>T</span> <span>clone</span><span>();</span>
<span>}</span>
public interface Prototype<T> { T clone(); }

Enter fullscreen mode Exit fullscreen mode

2.Define the Product interface that implements the Prototype with a creation constructor, copy constructor and the clone method implementation

<span>public</span> <span>class</span> <span>User</span> <span>implements</span> <span>Prototype</span><span><</span><span>User</span><span>></span> <span>{</span>
<span>private</span> <span>String</span> <span>name</span><span>;</span>
<span>private</span> <span>String</span> <span>lastName</span><span>;</span>
<span>private</span> <span>List</span><span><</span><span>String</span><span>></span> <span>roles</span><span>;</span>
<span>public</span> <span>User</span><span>(</span><span>String</span> <span>name</span><span>,</span> <span>String</span> <span>lastName</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>name</span> <span>=</span> <span>name</span><span>;</span>
<span>this</span><span>.</span><span>lastName</span> <span>=</span> <span>lastName</span><span>;</span>
<span>this</span><span>.</span><span>roles</span> <span>=</span> <span>new</span> <span>ArrayList</span><span><>();</span>
<span>}</span>
<span>// Copy constructor </span>
<span>public</span> <span>User</span><span>(</span><span>User</span> <span>source</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>name</span> <span>=</span> <span>source</span><span>.</span><span>name</span><span>;</span>
<span>this</span><span>.</span><span>lastName</span> <span>=</span> <span>source</span><span>.</span><span>lastName</span><span>;</span>
<span>this</span><span>.</span><span>roles</span> <span>=</span> <span>new</span> <span>ArrayList</span><span><>(</span><span>source</span><span>.</span><span>roles</span><span>);</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>User</span> <span>clone</span><span>()</span> <span>{</span>
<span>return</span> <span>new</span> <span>User</span><span>(</span><span>this</span><span>);</span>
<span>}</span>
<span>// Getters and setters</span>
<span>}</span>
<span>public</span> <span>class</span> <span>User</span> <span>implements</span> <span>Prototype</span><span><</span><span>User</span><span>></span> <span>{</span>
    <span>private</span> <span>String</span> <span>name</span><span>;</span>
    <span>private</span> <span>String</span> <span>lastName</span><span>;</span>
    <span>private</span> <span>List</span><span><</span><span>String</span><span>></span> <span>roles</span><span>;</span>

<span>public</span> <span>User</span><span>(</span><span>String</span> <span>name</span><span>,</span> <span>String</span> <span>lastName</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>name</span> <span>=</span> <span>name</span><span>;</span>
        <span>this</span><span>.</span><span>lastName</span> <span>=</span> <span>lastName</span><span>;</span>
        <span>this</span><span>.</span><span>roles</span> <span>=</span> <span>new</span> <span>ArrayList</span><span><>();</span>
    <span>}</span>


   <span>// Copy constructor </span>
    <span>public</span> <span>User</span><span>(</span><span>User</span> <span>source</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>name</span> <span>=</span> <span>source</span><span>.</span><span>name</span><span>;</span>
        <span>this</span><span>.</span><span>lastName</span> <span>=</span> <span>source</span><span>.</span><span>lastName</span><span>;</span>
        <span>this</span><span>.</span><span>roles</span> <span>=</span> <span>new</span> <span>ArrayList</span><span><>(</span><span>source</span><span>.</span><span>roles</span><span>);</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>User</span> <span>clone</span><span>()</span> <span>{</span>
        <span>return</span> <span>new</span> <span>User</span><span>(</span><span>this</span><span>);</span>
    <span>}</span>

    <span>// Getters and setters</span>
<span>}</span>
public class User implements Prototype<User> { private String name; private String lastName; private List<String> roles; public User(String name, String lastName) { this.name = name; this.lastName = lastName; this.roles = new ArrayList<>(); } // Copy constructor public User(User source) { this.name = source.name; this.lastName = source.lastName; this.roles = new ArrayList<>(source.roles); } @Override public User clone() { return new User(this); } // Getters and setters }

Enter fullscreen mode Exit fullscreen mode

3.Usage

<span>public</span> <span>class</span> <span>Main</span> <span>{</span>
<span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
<span>User</span> <span>originalUser</span> <span>=</span> <span>new</span> <span>User</span><span>(</span><span>"John"</span><span>,</span> <span>"Doe"</span><span>);</span>
<span>originalUser</span><span>.</span><span>addRole</span><span>(</span><span>"USER"</span><span>);</span>
<span>User</span> <span>clonedUser</span> <span>=</span> <span>originalUser</span><span>.</span><span>clone</span><span>();</span>
<span>}</span>
<span>}</span>
<span>public</span> <span>class</span> <span>Main</span> <span>{</span>
    <span>public</span> <span>static</span> <span>void</span> <span>main</span><span>(</span><span>String</span><span>[]</span> <span>args</span><span>)</span> <span>{</span>
        <span>User</span> <span>originalUser</span> <span>=</span> <span>new</span> <span>User</span><span>(</span><span>"John"</span><span>,</span> <span>"Doe"</span><span>);</span>
        <span>originalUser</span><span>.</span><span>addRole</span><span>(</span><span>"USER"</span><span>);</span>

        <span>User</span> <span>clonedUser</span> <span>=</span> <span>originalUser</span><span>.</span><span>clone</span><span>();</span>
    <span>}</span>
<span>}</span>
public class Main { public static void main(String[] args) { User originalUser = new User("John", "Doe"); originalUser.addRole("USER"); User clonedUser = originalUser.clone(); } }

Enter fullscreen mode Exit fullscreen mode

Advantages:
Reduces Subclassing & Constructor Complexity
Improves Maintainability

Limitations:
Deep Cloning Complexity

Conclusion:
The Prototype Pattern simplifies object creation by cloning existing instances, making it efficient for complex or expensive object initialization but requires careful handling of deep copies

Use it when object duplication is frequent and performance optimization is needed


Singleton Pattern

The Singleton Pattern is a ensures a class has only one instance accros the whole application and provides a global access point to that instance It restricts instantiation to a single object, making it ideal for managing shared resources like configurations, logging, and database connections ..etc

Problems:

  • the need of global accessibility without using global variables
  • Conflicting Instantiations
  • inconsistent states due to multiple instances and unnecessary resource consumption

solution
Ensures only one instance is created across threads

<span>// synchronized for thread safety </span>
<span>public</span> <span>class</span> <span>SingletonInstance</span> <span>{</span>
<span>private</span> <span>static</span> <span>volatile</span> <span>SingletonInstance</span> <span>instance</span><span>;</span>
<span>private</span> <span>SingletonInstance</span><span>()</span> <span>{}</span>
<span>public</span> <span>static</span> <span>SingletonInstance</span> <span>getInstance</span><span>()</span> <span>{</span>
<span>if</span> <span>(</span><span>instance</span> <span>==</span> <span>null</span><span>)</span> <span>{</span>
<span>synchronized</span> <span>(</span><span>SingletonInstance</span><span>.</span><span>class</span><span>)</span> <span>{</span>
<span>if</span> <span>(</span><span>instance</span> <span>==</span> <span>null</span><span>)</span> <span>{</span>
<span>instance</span> <span>=</span> <span>new</span> <span>SingletonInstance</span><span>();</span>
<span>}</span>
<span>}</span>
<span>}</span>
<span>return</span> <span>instance</span><span>;</span>
<span>}</span>
<span>}</span>
<span>// synchronized for thread safety </span>
<span>public</span> <span>class</span> <span>SingletonInstance</span> <span>{</span>
    <span>private</span> <span>static</span> <span>volatile</span> <span>SingletonInstance</span> <span>instance</span><span>;</span>

    <span>private</span> <span>SingletonInstance</span><span>()</span> <span>{}</span>

    <span>public</span> <span>static</span> <span>SingletonInstance</span> <span>getInstance</span><span>()</span> <span>{</span>
        <span>if</span> <span>(</span><span>instance</span> <span>==</span> <span>null</span><span>)</span> <span>{</span>
            <span>synchronized</span> <span>(</span><span>SingletonInstance</span><span>.</span><span>class</span><span>)</span> <span>{</span>
                <span>if</span> <span>(</span><span>instance</span> <span>==</span> <span>null</span><span>)</span> <span>{</span>
                    <span>instance</span> <span>=</span> <span>new</span> <span>SingletonInstance</span><span>();</span>
                <span>}</span>
            <span>}</span>
        <span>}</span>
        <span>return</span> <span>instance</span><span>;</span>
    <span>}</span>
<span>}</span>
// synchronized for thread safety public class SingletonInstance { private static volatile SingletonInstance instance; private SingletonInstance() {} public static SingletonInstance getInstance() { if (instance == null) { synchronized (SingletonInstance.class) { if (instance == null) { instance = new SingletonInstance(); } } } return instance; } }

Enter fullscreen mode Exit fullscreen mode

Advatages
Global Access Point
Lazy Initialization
Thread Safety
Reduces Memory Waste

Limitations
Breaks the Single Responsibility Principle by being responsible for both creating and managing its own instance
Hinders Unit Testing

Conclusion
The Singleton Pattern ensures a single instance of a class, useful for shared resources like logging and databases

Comparison of Creational Design Patterns

Pattern Purpose Key Benefits Common Use Cases
Builder Constructs complex objects step by step Readability, Immutability, Flexibility UI Builders, Query Builders, API Clients
Factory Encapsulates object creation logic Loose Coupling, Scalability Dependency Injection, Payment Processing
Abstract Factory Creates families of related objects Consistency, Encapsulation UI Themes, Cross-Platform Development
Prototype Clones existing objects Performance Optimization, Simplifies Instantiation Game Development, Document Cloning
Singleton Ensures a single instance Global Access, Controlled Instantiation Logging, Database Connections

Conculision

Creational design patterns provide flexible, scalable, and maintainable ways to create objects in software development. Whether you’re dealing with complex object creation (Builder Pattern), ensuring a single instance (Singleton Pattern), or managing families of related objects (Abstract Factory), these patterns help structure your code effectively.

By understanding these patterns, you can improve your software architecture and write cleaner, more reusable code.

原文链接:Design patterns – Creational Patterns

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
I am ordinary yet unique.
我很平凡,但我独一无二
评论 抢沙发

请登录后发表评论

    暂无评论内容