A class
can be accessed by its objects
or instance
only, which requires a Constructor
to create it where values will be passed. a Constructor
can be either No Argument
or Default Constructor
, some Required Argument Constructor
, or All Arguments Constructor
.
but Constructors
have Fixed Positional Parameters
, either value should be passed with a specific order
or null
to fill the position. or a New Constructor
must be created with the required values, with the addition of a new attribute, another Constructor
would be required. this is also called Telescoping Constructors Problem
.
Builder Design Pattern
helps to solve this issue, instead of passing values into the Constructor
, the Builder Design Pattern
provides specific methods to set the value and a build
method to combine all the passed values to create an object
. it minimizes the requirement of passing non-required parameters
and increases readability.
The Builder Pattern is used to construct a complex object with a step-by-step build process.
since the Builder Pattern
doesn't provide setters
so it makes the object immutable
up to some extent.
package org.wesome.design.patterns;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Getter
@ToString
public class Apple {
private final String name;
private final String taste;
Apple(String name, String taste) {
this.name = name;
this.taste = taste;
}
public static AppleBuilder builder() {
return new AppleBuilder();
}
@NoArgsConstructor
public static class AppleBuilder {
private String name;
private String taste;
public AppleBuilder name(String name) {
this.name = name;
return this;
}
public AppleBuilder taste(String taste) {
this.taste = taste;
return this;
}
public Apple build() {
return new Apple(name, taste);
}
public Apple.AppleBuilder copy(Apple o) {
Apple.AppleBuilder copiedBuilder = this.name(o.getName()).taste(o.getTaste());
return copiedBuilder;
}
}
}
package org.wesome.design.patterns;
public class Builder {
public static void main(String[] args) {
Apple macintosh = Apple.builder().build();
System.out.println("macintosh " + macintosh);
Apple macintoshCopy = Apple.builder().copy(macintosh).build();
System.out.println("macintoshCopy " + macintoshCopy);
Apple fuji = Apple.builder().name("Fuji").build();
System.out.println("fuji = " + fuji);
Apple fujiCopy = Apple.builder().copy(fuji).name("Fuji").build();
System.out.println("fujiCopy = " + fujiCopy);
Apple gala = Apple.builder().name("Gala").taste("sour").build();
System.out.println("gala = " + gala);
Apple galaCopy = Apple.builder().copy(gala).name("Gala").taste("sour").build();
System.out.println("galaCopy = " + galaCopy);
}
}
package org.wesome.design.patterns;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class BuilderTest {
@Test
void testBuilderWithAllNullParameters() {
Apple macintosh = Apple.builder().build();
Assertions.assertAll(() -> {
Assertions.assertNotNull(macintosh);
Assertions.assertNull(macintosh.getName());
Assertions.assertNull(macintosh.getTaste());
});
}
@Test
void testBuilderWithSomeNullParameters() {
Apple fuji = Apple.builder().name("Fuji").build();
Assertions.assertAll(() -> {
Assertions.assertNotNull(fuji);
Assertions.assertNotNull(fuji.getName());
Assertions.assertNull(fuji.getTaste());
});
}
@Test
void testApple() {
Apple gala = Apple.builder().name("Gala").taste("sour").build();
Assertions.assertAll(() -> {
Assertions.assertNotNull(gala);
Assertions.assertNotNull(gala.getName());
Assertions.assertNotNull(gala.getTaste());
});
}
}
plugins {
id 'java'
id "io.freefair.lombok" version "6.2.0"
}
group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}
test {
useJUnitPlatform()
}
Builder Design Pattern
increases the lines of code and each class needs to define another static build class
.