본문 바로가기
  • 일하면서 배운 내용 끄적이는 블로그
Java

빌더 패턴 (Builder Pattern)

by dhl7799 2024. 7. 9.

빌더패턴은 객체를 생성하는 방법중 하나로 (이외엔 생성자 패턴, 정적 메소드 패턴, 수정자 패턴이 있다)

 

장점은 다음과 같다

 

1. 필요한 데이터만 설정
2. 유연성
3. 가독성
4. 변경 가능성을 최소화

예제

public class Car {
    private String wheels;
    private String engine;
    private String color;

    private Car() {}

    public String getWheels() {
        return wheels;
    }

    public String getEngine() {
        return engine;
    }

    public String getColor() {
        return color;
    }

    @Override
    public String toString() {
        return "Car with " + wheels + " wheels, " + engine + " engine, and " + color + " color.";
    }

    public static class Builder {
        private String wheels;
        private String engine;
        private String color;

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

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

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

        public Car build() {
            Car car = new Car();
            car.wheels = this.wheels;
            car.engine = this.engine;
            car.color = this.color;
            return car;
        }
    }
}

메인

public class Main {
    public static void main(String[] args) {
        // 스포츠카 생성
        Car sportsCar = new Car.Builder()
                .setWheels("Sports Wheels")
                .setEngine("V8 Engine")
                .setColor("Red")
                .build();
        System.out.println(sportsCar);

        // SUV 생성
        Car suvCar = new Car.Builder()
                .setWheels("SUV Wheels")
                .setEngine("V6 Engine")
                .setColor("Black")
                .build();
        System.out.println(suvCar);
    }
}

필요한 데이터만 설정

만약 해당 객체를 생성자 패턴으로 생성한다고 하면, 색상값이 필요 없을때

Car sportsCar = new Car("Sports Wheels", "V8 Engine", null);

 

이런식으로 필요없는 부분도 선언을 해주어야 하지만, 빌더 패턴을 사용할 경우

Car suvCar = new Car.Builder()
	.setWheels("SUV Wheels")
	.setEngine("V6 Engine")
	.build();

 

간단하게 필요없는 부분을 제외하면 된다.

유연성

방금같은 경우는 필요없는 속성을 제외시켰지만, 반대로 새로운 속성을 추가한다면 어떻게 될까

예를들어 브랜드를 추가한다고 쳤을때

 

생성자 방식은 존재하는 모든 객체 선언에 새로운 패러미터를 추가해줘야 한다

Car sportsCar = new Car("Sports Wheels", "V8 Engine", "Red", "HyunDai");

 

추가해주지 않으면 추가한 새 속성이 필요없는 상황이라고 해도 해당부분에서 에러가 발생하게 된다

따라서 의미없는 수정을 해야한다

Car sportsCar = new Car("Sports Wheels", "V8 Engine", null, null);

 

반면 기존에 빌더패턴으로 선언해 두었다면 굳이 필요없는 상황에서는 객체를 생성하는 코드를 수정하지 않아도 된다.

가독성

Car sportsCar = new Car("Sports Wheels", "V8 Engine", "Red", "HyunDai");

 

그나마 이렇게 패러미터가 어떤 의미인지 한눈에 보이는 상황에선 가독성이 괜찮지만

Person man = new Person(180, 100, 100, 28);

 

이런식으로 각 패러미터가 어떤걸 의미하는 값인지 알수가 없는 상황이 많이 존재한다. 

특히 속성이 3개 이상이라면 더더욱 가독성이 떨어진다.

 

이때 빌더 패턴을 사용하게 되면

Person man = new Person.Builder()
	.setHeight(180)
	.setWeight(100)
	.setIq(100)
	.setAge(28)
	.build();

 

각 패터미터가 어떤 의미로 들어가는 값인지 한눈에 볼 수 있다.

변경 가능성을 최소화

이건 사실상 수정자 패턴이랑 비교한것으로 (속성을 Setter로 설정하는거)

속성에 final 붙이고 가능하면 변경하지 않는걸 의미

 

@Builder 어노테이션

위에선 빌더를 일일이 선언했지만

빌더 어노테이션을 사용하면 간단하게 구현할 수 있다.

import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

@Builder
@Getter
@ToString
public class Car {
    private String wheels;
    private String engine;
    private String color;
}

public class Main {
    public static void main(String[] args) {
        Car car = Car.builder()
                     .wheels("Sports Wheels")
                     .engine("V8 Engine")
                     .color("Red")
                     .build();
        
        System.out.println("Wheels: " + car.getWheels());
        System.out.println("Engine: " + car.getEngine());
        System.out.println("Color: " + car.getColor());
    }
}