Java

[Java] 빌더 패턴 (+ @Builder)

공대생안씨 2024. 9. 15. 17:21

1. 빌더 패턴 (Builder Pattern)

  • 객체 생성 시 단계별로 생성 가능하게 도와주는 디자인 패턴
  • 객체의 생성 과정을 분리함 ⇒ 다양한 구성요소를 조합하여 객체 생성
    • ex) 이름, 나이, 학년, 과목, 학점 등을 가지는 Person 클래스 ⇒ 이름, 나이만 갖는 객체 / 이름, 나이, 학년만 갖는 객체 등
  • 주로 매개변수가 많은 생성자, 불변객체 등을 만들때 유용함

 

1-1. 빌더 패턴 예제

  • Person.java
@ToString
public class Person {

    private String name;
    private int age;
    private double grade;

    // 빌더 사용 생성자
    public Person(PersonBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.grade = builder.grade;
    }

    // 빌더 클래스
    public static class PersonBuilder {
        private String name;
        private int age;
        private double grade;

        public PersonBuilder setName(String name) {
            this.name = name;
            return this;
        }

        public PersonBuilder setAge(int age) {
            this.age = age;
            return this;
        }

        public PersonBuilder setGrade(double grade) {
            this.grade = grade;
            return this;
        }

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

 

  • Main.java
public class Main {
    public static void main(String[] args) {
        Person person1 = new Person.PersonBuilder()
                .setName("이름1")
                .setAge(20)
                .setGrade(4.3)
                .build();

        Person person2 = new Person.PersonBuilder()
                .setName("이름2")
                .setAge(20)
                .build();

        Person person3 = new Person.PersonBuilder()
                .setGrade(4.5)
                .build();

        System.out.println("person1 = " + person1.toString());
        System.out.println("person2 = " + person2.toString());
        System.out.println("person3 = " + person3.toString());
    }
}

/* 실행 결과
person1 = Person(name=이름1, age=20, grade=4.3)
person2 = Person(name=이름2, age=20, grade=0.0)
person3 = Person(name=null, age=0, grade=4.5)*/

 

1-2. 빌더 패턴 장점

1. 가독성 향상

// 필드를 많이 갖는 클래스라면 생성자로 객체 생성 시 각각의 파라미터가 무엇을 의미하는지 헷갈림
Person person = new Person("이름1", 20, 4.3, 180, ... , "email@gmail.com");

// 빌더 패턴 사용하면 어떤 파라미터인지 명확함!
Person person = Person.builder()
				.name("이름1")
				.age(20)
				.grade(4.3)
				.height(180) ... .build();

 

2. 필요한 필드만 사용 가능

  • 생성자로만 객체 생성한다면 필요한 필드 조합에 해당하는 생성자를 모두 만들어야 함 (혹은 더미값을 넘김)
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    // 이름만 갖는 생성자
		
    // 이름, 나이만 갖는 생성자
		
    // 이름, 학점만 갖는 생성자
		
    ...
}
  • 빌더 패턴 사용한다면 빌더에 해당하는 코드만 생성하면 다양한 필드 조합으로 객체 생성 가능함!

 

1-3. 빌더 패턴 단점

  1. 빌더에 해당하는 추가적인 클래스를 생성해야 함
    @Builder로 해결 가능!
  2. 추가적인 메모리 사용
    • 빌더 객체를 별도로 생성함 ⇒ 메모리 사용량 증가

 

2. @Builder로 빌더 패턴 구현하기

  • 위의 예제에 해당하는 Person.java를 @Builder 사용 방식으로 변경

 

2-1. Lombok 의존성 추가

  • build.gradle
dependencies {
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
}

 

2-2. 클래스에 @Builder 어노테이션 추가

  • Person.java 수정
@ToString
@Builder
public class Person {

    private String name;
    private int age;
    private double grade;
}

@Builder 어노테이션이 빌더 사용 생성자, 빌더 클래스 등의 코드를 자동으로 생성함!!

 

  • Main.java
public class Main {
    public static void main(String[] args) {

        Person person1 = Person.builder()
                .name("이름1")
                .age(20)
                .grade(4.3).build();

        Person person2 = Person.builder()
                .name("이름2")
                .age(20).build();

        Person person3 = Person.builder()
                .grade(4.5).build();

        System.out.println("person1 = " + person1.toString());
        System.out.println("person2 = " + person2.toString());
        System.out.println("person3 = " + person3.toString());
    }
}

/* 실행 결과 (위의 예제와 동일)
person1 = Person(name=이름1, age=20, grade=4.3)
person2 = Person(name=이름2, age=20, grade=0.0)
person3 = Person(name=null, age=0, grade=4.5)*/