본문 바로가기
Back-End/Java

생성자에 매개변수가 많다면 빌더를 고려하라 | Builder pattern, Lombok @builder

by 달의 조각 2022. 10. 15.

 

Builder pattern

 

https://refactoring.guru/design-patterns/builder

 

빌더 패턴은 "복잡한 객체의 구성과 그 표현을 분리하여 동일한 구성 과정이 서로 다른 표현을 만들 수 있도록 하는 것"을 목표로 한다. 복잡한 개체를 단계별로 구성하는 데 사용되며, 마지막에는 개체를 반환한다.

생성자에 대한 매개변수가 축소되고, 가독성이 높은 메서드 호출로 제공된다. 때문에 선택적 매개변수에 대해 null을 전달할 필요가 없다. 객체는 완전한 상태로 인스턴스화되며, 불변 객체는 복잡한 논리 없이 구축될 수 있다. 코드 라인 수는 두 배 이상으로 증가하지만, 가독성 있는 코드를 작성할 수 있다.

 

유용한 예시

Member 클래스가 있다고 가정하자. name, age, email, phone 필드를 갖고 아래의 특성을 갖는다.

  • 한 번 생성되면 읽기만 가능해야 한다.
  • setter 메서드는 존재하지 않고, 생성자로 데이터를 입력받는다.
  • email과 phone 정보는 필수가 아니다.

email과 phone 정보를 전달하지 않는 Member 객체는 아래와 같이 생성한다.

new Member("JYJ", 24, null, null);

위의 코드는 가독성이 좋지 않으므로 새로운 생성자를 만든다면 아래와 같다. 만약 email을 가지고 phone 정보를 전달하지 않는다면 또 다른 생성자를 생성해야 할까? 또한 같은 String 타입인 email과 phone 정보를 실수로 반대로 넣는다면 이를 찾아내기 힘들 것이다.

public Member(String name, Integer age) {
    this(name, age, null, null);
}

 

빌더 패턴 적용

  • 불필요한 생성자를 만들지 않고 객체를 만든다.
  • 데이터 순서에 상관없이 객체를 만든다.
  • 사용자가 봤을 때 명시적이고 이해할 수 있어야 한다.
public class BuilderPattern {
    public static void main(String[] args) {
        
        MemberBuilder memberBuilder = new MemberBuilder();
        
        Member result = MemberBuilder
                .setName("JJK")
                .setAge(24)
                .setEmail("yjung.dev@gmail.com")
                .setPhone("010-1234-5678")
                .setName("JYJ") // 같은 메소드를 호출하면 나중에 호출한 값이 들어간다
                .setFavoriteNumber(7)
                .build(); // 최종 결과물을 생성하고 반환한다
        // "name: JYJ, age: 24, email: yjung.dev@gmail.com, phone: 010-1234-5678"
        System.out.println(result.getMember());
    }
}

 

빌더 클래스를 분리할 필요 없이 객체로 만들 클래스 내부에 포함시킬 수 있다.

Member member = Member.Builder()
    .setName("JYJ")
    .setAge(24)
    .build();
public static MemberBuilder Builder() { // static 선언 필소
    return new MemberBuilder();
}

 

 

Project Lombok's @Builder

먼저 GoF의 빌더 패턴과 같지 않다는 점을 유의한다. 클레스나 생성자 레벨에 붙여 주면 Builder 패턴을 자동으로 생성해 줘서 유용하다.

클래스 레벨에 추가하면 내부 정적 빌더 클래스로 해당 클래스의 모든 인스턴스 필드에 대한 빌더를 생성한다. 모든 매개변수가 있는 생성자를 생성하는 @AllArgsConstructor와 사용 시 중복이 되므로 함께 사용하지 않아야 한다. 넘겨 받지 않아야 할 데이터가 있을 경우 모든 생성자를 열어 두는 것은 좋은 방법이 아니다.

생성자나 메서드 레벨에 추가하면 생성자 매개변수만 포함하는 빌더 클래스를 생성한다. 특정 필드에 대한 빌더를 만들기 위한 방법이다. Builder 사용 시 매개변수를 최소화하는 것이 바람직하다.

@Builder
public class Member {
    private final String name;
    private final Integer age;
    private final String email;
    private final String phone;
}
Member member = Member.builder()
    .name("JYJ")
    .age(24)
    .build();

 

 

📚 Reference

 

'Back-End > Java' 카테고리의 다른 글

[Design Pattern] 디자인 패턴의 정의와 종류  (0) 2022.09.25
Optional이란?  (0) 2022.09.01
StringBuilder의 사용, String으로 변환  (0) 2022.08.13
StringifyJSON  (0) 2022.07.22
스레드(Thread)  (0) 2022.07.19

댓글