본문 바로가기

Computer Science/Design Pattern

[Design Pattern] 빌더 패턴(Builder Pattern)에 대해서, @Builder - 컴도리돌이

728x90

객체를 생성할 때, 보통 다음과 같이 코드를 작성할 것입니다. 

 

public void builderPattern() {
    Car car = new Car();
    ...
}

 

하지만 객체를 생성할 때는, 빈 깡통으로 사용하기보다는 그 객체의 고유한 값을 갖은 상태로 객체를 사용할 것입니다. 그렇기 때문에 객체를 생성할 때는 파라메터를 전달하여 생성해야겠죠. 생성할 때 필요한 매개변수를 입력하여 다음과 같이 코드를 작성합니다. 

 

public void builderPattern() {
    String param1 = "Chevrolet";
    String param2 = "RS";
    int param3 = 2024;
    
    ...
    
    Car car = new Car(param1, pararm2, pararm3, ...);
    
    ...
}

 

위에 코드처럼 파라메터를 입력하여 생성자를 생성할 수 있습니다. 그렇지만 값이 필수 값이 아니면 어떻게 해야 할까요?

빈 값을 넣거나, null을 전달해줘야 합니다. 숫자의 경우라면 -1와 같이 대게 사용하지 않은 값을 설정합니다. 

 

아니면 다음과 같이 생성자 오버로딩 할 수 있습니다.

 

@Data
public class Car {
    private String brand;
    private String model;
    private int year;
    private String color;
    private int price;

    public Car(String brand, String model, int year, String color, int price) {
        this.brand = brand;
        this.model = model;
        this.year = year;
        this.color = color;
        this.price = price;
    }
    
    public Car(String brand, String model, int year) {
        this.brand = brand;
        this.model = model;
        this.year = year;
    }
}

 

생성자 오버로딩은 동일한 이름을 가진 생성자가 서로 다른 매개변수 목록을 가지고 있는 것을 의미하며, 이를 통해 다양한 매개변수 조합에 따라 객체를 생성할 수 있습니다. 

 

그렇다면 위에 생성자를 오버로딩할 때 생기는 문제가 뭐가 있을까요?? 🤔

 

많은 매개변수를 가진 생성자들이 만들어질 때, 가독성을 저하시키고, 어떤 생성자가 어떤 매개변수를 필요로 하는지 혼란을 줍니다. 또한 생성자의 매개변수 순서나 타입을 바꾸려면 해당 생성자를 호출하는 코드 전체를 변경해야 해요. 까딱 방심하기만 하면 버그를 발생시킬 가능성이 높아지죠. 마지막으로 선택적 매개변수를 처리하기 어렵습니다. 만약 매개변수 중 하나가 선택적이면, 해당 생성자를 추가하거나 기본 값을 설정하는 방법밖에 없습니다. 

 


 

빌더 패턴(Builder Patterns)

이번에 포스팅할 빌더 패턴은 복잡한 객체를 생성할 때 사용되며, 객체의 생성 과정을 단순화하고 가독성을 높이는 데  도움이 됩니다. 

위에 차 객체를 빌더 패턴을 적용하여 다음과 같이 코드를 작성할 수 있습니다. 

 

빌더 패턴을 적용한 코드 예시

@Data
public class Car {
    private String brand;
    private String model;
    private int year;
    private String color;
    private int price;

    private Car(Builder builder) {
        this.brand = builder.brand;
        this.model = builder.model;
        this.year = builder.year;
        this.color = builder.color;
        this.price = builder.price;
    }

    public static Builder builder(String brand, String model, int year) {
        return new Builder(brand, model, year);
    }

    public static class Builder {
        private String brand;
        private String model;
        private int year;
        private String color;
        private int price;

        private Builder(String brand, String model, int year) {
            this.brand = brand;
            this.model = model;
            this.year = year;
        }

        public Builder color(String color) {
            this.color = color;
            return this;
        }

        public Builder price(int price) {
            this.price = price;
            return this;
        }

        public Car build() {
            return new Car(this);
        }
    }

    // Getters for Car attributes
}

 

위에 빌더 패턴을 적용한 코드를 이제 다음과 같이 사용할 수 있습니다. 

 

// 모든 필드를 설정한 예시
Car car1 = Car.builder("Toyota", "Camry", 2022)
               .color("Red")
               .price(30000)
               .build();

// 일부 필드만 설정한 예시
Car car2 = Car.builder("Ford", "Mustang", 2018)
               .color("Blue")
               .build();

 

초기에 자동차 브랜드와 모델 그리고 연식이 기본 값이었고, 색깔과 가격을 필수 값이 아니었죠. 이제 생성자를 생성할 때 빈 값 또는 null, 쓰레기 값을 파라메터를 전달할 필요 없이 필요한 값만 지정해서 객체를 생성할 수 있습니다. 

 

즉, 선택적으로 매개 변수를 처리할 수 있으며, 매개 변수의 순서를 신경 쓰지 않고 객체를 생성할 수 있습니다. 그리고 무엇보다 가독성이 높아집니다. 

 


 

@Builder 어노테이션을 사용한 예시 코드

@Data
@Builder
public class Car {
    private String brand;
    private String model;
    private int year;
    private String color;
    private int price;
}

 

위에는 롬복에 있는 빌더 어노테이션을 사용한 예시 코드입니다. 단순히 어노테이션만 달면 빌더가 생기고, 다음과 같이 생성할 수 있다. 

 

// 모든 필드를 설정한 예시
Car car1 = Car.builder()
               .brand("Toyota")
               .model("Camry")
               .year(2022)
               .color("Red")
               .price(30000)
               .build();

// 일부 필드만 설정한 예시
Car car2 = Car.builder()
               .brand("Ford")
               .model("Mustang")
               .year(2018)
               .color("Blue")
               .build();

 

위에 빌더 패턴을 적용한 코드와 동일한 작업을 하는데, 더욱 간단하게 코드를 작성하고 사용할 수 있습니다. 또한 가독성이 더욱 좋아졌습니다. 

 

물론 lombok documents에서는 @Builder 어노테이션을 생성자에 추가하라고 명시하고 있습니다. 

 

 

@Builder

 

projectlombok.org

 

왜 그럴까요? 위에 문서를 보면 

 

Finally, applying @Builder to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the @Builder annotation to this all-args-constructor.

 

@Builder 어노테이션을 붙이는 것은 모든 매개변수를 가지는 생성자에 대해 빌더를 생성하도록 되어있습니다. 생성자 위에 Builder 어노테이션을 붙이면 해당 생성자에 대한 빌더 클래스가 생성되고, 클래스의 생성자에 대해 빌더 패턴을 사용할 때와 동일한 장점을 얻을 수 있습니다. 이는 코드의 일관성을 유지하고 유지 관리를 쉽게 해 줍니다. 

 

생성자 오버로딩해서 객체를 생성하는 부분을 많이 보였는데, 다음 기회에 빌더 패턴을 적용해서 해당 코드를 수정해 봐야겠어요 😓 

오늘도 재미있는 디자인 패턴을 배우게 되어 뜻깊은 날이었네요 😊

 


 

Design Pattern | 빌더 패턴(Builder Pattern)

빌더 패턴이란?빌더 패턴(Builder Pattern)은 객체 생성을 단순화하고 유연성을 높이기 위한 디자인 패턴입니다. 복잡한 객체의 생성 과정을 추상화하고 객체의 구성 요소를 조립하여 객체를 생성

yujin-17.tistory.com

 

???: 빌더 패턴은 필수 값을 받지 못하잖아요.

Lombok에서 지원하는 @Builder를 사용하다보면, 필수 값을 빼먹어서 곤란했던 적이 있을 것이다. 그래서 나는 솔직히 Builder를 안티패턴 취급하며, 협업에서는 지양해야 한다고, 혼자 마음 속으로 생

jacobhboy66.tistory.com

 

@Builder

 

projectlombok.org