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.
暂无评论内容