[Lombok] @Builder Class vs Constructor-Level

ichubtou·2024년 1월 17일
0

@Builder


  • 필요한 인자만 설정 가능
    • 위 장점은 Class위에 선언하냐 Constructor 위에 선언하냐에 따라 달라짐
  • 유연성 확보
    • 새롭게 추가되는 변수 때문에 기존의 코드를 수정해야 하는 상황이 발생했을 때, 기존의 모든 코드를 수정해야함
    • 빌더 패턴을 이용하면 새로운 변수가 추가되는 등의 상황에도 기존의 코드에 영향 X
  • 가독성을 높일 수 있음
    • 생성자 파라미터가 많을 경우 가독성이 좋지 않음
    • 빌더 패턴을 사용하면 매개 변수가 많아져도 가독성을 높일 수 있음
  • 순서가 바뀐다면 값도 바뀌어버림
  • 생성자를 활용해 객체를 생성할 때는 매개변수의 순서가 중요
    • @Builder 는 이 문제를 해소
  • 빌더는 생성자가 없을때 모든 인자를 포함한 생성자를 생성
    • JPA를 사용한다면 @NoArgsConstructor 를 선언하기 때문에 추가적으로 생성해줘야 함
    • 보통 @AllArgsConstructor 많이 사용
  • @Builder 를 Class 위에 선언
    • 장점
      • 모든 필드에 대해 자동으로 빌더 메서드가 생성
      • 코드가 간결해지며, 모든 필드를 포함하는 통합된 빌더 인터페이스를 제공
      • 선택적 필드에 대해 유연성이 높아짐
    • 단점
      • 객체 생성 시 받지 않아야 할 매개변수들도 빌더에 노출 됨
      • 필수 필드와 선택 필드를 구분하기 어려움, 별도의 검증 로직이 필요
  • @Builder 를 Constructor 위에 선언하기
    • 장점
      • 필요한 필드만을 포함하는 명시적인 빌더를 생성
      • 필수 필드가 명확해지며, 해당 필드들을 포함하는 빌더를 생성하기 쉬움
    • 단점
      • 각기 다른 필드 조합에 대해 별도의 생성자를 정의해야 할 수 있음
      • 코드가 더 복잡해지며, 여러 생성자에 @Builder를 적용해야 할 수 있음
  • 결정 요소
    • 필수 필드와 선택 필드의 구분
      • 필수 필드와 선택 필드를 명확히 구분해야 한다면, 생성자 레벨에서 @Builder 를 사용하는 것이 좋음
    • 유연성
      • 모든 필드에 대해 높은 수준의 유연성을 원한다면, 클래스 레벨에서 @Builder 를 사용하는 것이 바람직
    • 복잡성
      • 클래스에 필드가 많고 복잡한 경우, 클래스 레벨에서 @Builder 를 사용하여 복잡성을 관리
    • 사용 사례
      • 특정 필드 조합으로만 객체를 생성해야 하는 경우, 생성자 레벨에서 @Builder 를 사용하는 것이 유리
      • 모든 필드에 대해 객체를 구성하고 싶다면 클래스 레벨이 적합
    • 코드 유지보수
      • 클래스 레벨에서 @Builder를 사용하면 코드가 더 간결해지고 유지보수가 용이할 수 있음
      • 생성자 레벨에서 사용하면 더 많은 코드를 작성해야 하지만, 객체 생성 시 더 명확한 가이드라인 제공
  • 결론
    • 일반적으로는 클래스 레벨에 @Builde 를 적용하는 것이 더 일반적이고 간편하지만, 복잡한 도메인 모델이나 염격한 객체 생성 규칙을 가진 경우에는 생성자 레벨에서의 적용이 더 적합
  • @Builder 를 Constructor 위에 선언 - 필수값 체크 방법
    • 이 방법은 런타임에서만 필수 필드의 누락 여부를 확인 가능
    • 컴파일 타임에 필수 필드의 존재를 강제 X
    1. Assert

      	public class Book {
      
          private String bookName;
          private String author;
          private Integer price;
      
          @Builder
          public Book(String bookName, String author, Integer price) {
      				Assert.hasText(bookName, "책이름이 없습니다");
              Assert.hasText(author, "저자명이 없습니다");
      				//Assert.notNull(price, "가격이 설정되지 않았습니다");
              this.bookName = bookName;
              this.author = author;
              this.price = price;
          }
      }

      Exception in thread "main" java.lang.IllegalArgumentException: 책이름이 없습니다

    2. @NonNull

      @Builder
      public Book(@NonNull String bookName, @NonNull String author, Integer price) {
      		this.bookName = bookName;
          this.author = author;
          this.price = price;
      }

      Exception in thread "main" java.lang.NullPointerException: bookName is marked non-null but is null

      • 메세지 커스텀 불가
  • @Builder 를 Constructor 위에 선언하고 @NonNull을 사용하는게 적합해보임
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Member {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; // 선택적 필드

    @NonNull
    private String name; // 필수 필드

    @NonNull
    private Integer age; // 필수 필드

    @Builder
    public Member(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

0개의 댓글