Java builder pattern baeldung example

Loren 35 Published: 09/02/2024

Java builder pattern baeldung example

I'm happy to respond in English for you today!

The Builder pattern is a creational design pattern that allows you to construct complex objects step by step, giving you control over the process of object creation.

Let's dive into an example using Java and Baeldung's article on this topic.

Imagine we want to create a Car object. We can start with a simple constructor-based approach:

public class Car {

private String make;

private int year;

public Car(String make, int year) {

this.make = make;

this.year = year;

}

}

This works fine for small objects like Car, but what if our object is more complex and has many optional properties? For example, we might want to add features like air conditioning, GPS, and so on.

In this case, a constructor-based approach becomes cumbersome. We would need to create multiple constructors with many parameters, which leads to a maintenance nightmare:

public class Car {

private String make;

private int year;

public Car(String make, int year) {

this.make = make;

this.year = year;

}

public Car(String make, int year, boolean airConditioning) {

this(make, year);

this.airConditioning = airConditioning;

}

public Car(String make, int year, boolean airConditioning, boolean gps) {

this(make, year, airConditioning);

this.gps = gps;

}

// ...

}

The Builder pattern comes to the rescue here. It separates the construction of an object from its representation.

We create a separate CarBuilder class that encapsulates the logic for creating a Car:

public class Car {

private String make;

private int year;

public Car(CarBuilder builder) {

this.make = builder.make;

this.year = builder.year;

}

}

public class CarBuilder {

private String make;

private int year;

private boolean airConditioning;

private boolean gps;

public CarBuilder(String make, int year) {

this.make = make;

this.year = year;

}

public CarBuilder withAirConditioning(boolean airConditioning) {

this.airConditioning = airConditioning;

return this;

}

public CarBuilder withGPS(boolean gps) {

this.gps = gps;

return this;

}

public Car build() {

return new Car(this);

}

}

Now we can create a Car step by step:

public class Main {

public static void main(String[] args) {

Car car = new CarBuilder("Ford", 2022)

.withAirConditioning(true)

.withGPS(false)

.build();

}

}

This code creates a Car with the specified make, year, air conditioning, and GPS. The CarBuilder takes care of setting the properties correctly.

By using the Builder pattern, we decouple the construction process from the object's representation. This makes our code more maintainable and scalable when dealing with complex objects and many optional properties.

Thanks for your patience, and I hope this explanation helped you understand the Builder pattern better!

Stay tuned for more design patterns and programming tips!

What are creational patterns in Java?

Creational design patterns in Java are concerned with the process of creating objects. They help to define the way objects are created, composed, and related to each other. Here are some common creational design patterns:

Singleton Pattern: This pattern ensures that only one instance of a class is created. The Singleton pattern is used when you want to ensure that only one object of its kind exists or has been instantiated.

In Java, the Singleton pattern can be implemented using a private constructor and static methods for creating and retrieving instances. Here's an example:

public class Singleton {

private static Singleton instance;

private Singleton() {}

public static synchronized Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

Factory Pattern: This pattern provides a way to create an object without specifying the exact class of which it is an instance.

In Java, the Factory pattern can be implemented using abstract classes and interfaces. Here's an example:

public abstract class Animal {

}

public interface AnimalFactory {

Animal createAnimal();

}

public class DogFactory implements AnimalFactory {

public Animal createAnimal() {

return new Dog();

}

}

public class CatFactory implements AnimalFactory {

public Animal createAnimal() {

return new Cat();

}

}

Abstract Factory Pattern: This pattern provides a way to create families of related objects without specifying their exact classes.

In Java, the Abstract Factory pattern can be implemented using abstract classes and interfaces. Here's an example:

public abstract class AnimalFactory {

public abstract Animal createAnimal();

}

public class DogFactory implements AnimalFactory {

public Animal createAnimal() {

return new Dog();

}

}

public class CatFactory implements AnimalFactory {

public Animal createAnimal() {

return new Cat();

}

}

Prototype Pattern: This pattern provides a way to create objects by copying existing ones.

In Java, the Prototype pattern can be implemented using cloneable objects and interfaces. Here's an example:

public interface Cloneable {

public Object clone();

}

public class Animal implements Cloneable {

@Override

public Object clone() throws CloneNotSupportedException {

// implement cloning logic here

}

}

Builder Pattern: This pattern provides a way to construct complex objects step by step.

In Java, the Builder pattern can be implemented using immutable classes and setter methods. Here's an example:

public class Person {

private String name;

private int age;

public static class Builder {

private String name;

private int age;

public Builder(String name) {

this.name = name;

}

public Builder setAge(int age) {

this.age = age;

return this;

}

public Person build() {

return new Person(this);

}

}

private Person(Builder builder) {

this.name = builder.name;

this.age = builder.age;

}

@Override

public String toString() {

return "Person{" +

"name='" + name + ''' +

", age=" + age +

'}';

}

}

Interface Segregation Principle (ISP) Pattern: This pattern suggests that clients should not be forced to depend on interfaces they don't use.

In Java, the ISP pattern can be implemented by breaking down large interfaces into smaller ones. Here's an example:

public interface AnimalSound {

void makeSound();

}

public interface CanFly {

void fly();

}

public class Bird implements AnimalSound, CanFly {

@Override

public void makeSound() {

// implement sound making logic here

}

@Override

public void fly() {

// implement flying logic here

}

}

In conclusion, creational design patterns in Java help to create objects and their relationships without specifying the exact classes. They provide a way to define the way objects are created, composed, and related to each other. By using these patterns, you can write more flexible, maintainable, and scalable code.