관리 메뉴

기록하는 공간

빌더 패턴(Builder Pattern)은 무엇인가 본문

Spring

빌더 패턴(Builder Pattern)은 무엇인가

giwoong01 2023. 11. 29. 17:49

빌더 패턴…

바로 예시부터 보면서 확인한다.

예시) 실제 사용 중인 코드

Board를 생성한다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "board_id")
    @Schema(description = "아카이빙 Id", example = "1")
    private Long boardId;

    @Schema(description = "아카이빙 제목", example = "강아지와 산책한 날")
    private String boardTitle;

    @Schema(description = "추억의 날짜", example = "2023-11-14")
    private String boardTime;

    @Schema(description = "추억의 장소", example = "집 앞 공원")
    private String boardPlace;

    @Schema(description = "아카이빙 테그", example = "0")
    private int boardTag;

    @Schema(description = "아카이빙 내용", example = "아카이빙 내용")
    private String boardContent;

    @OneToMany(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Comment> comments = new ArrayList<>();

    @OneToMany(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<MemberWriteBoard> memberWriteBoards = new ArrayList<>();

    @Builder
    private Board(String boardTitle, String boardPlace, int boardTag, String boardContent, String boardTime) {
        this.boardTitle = boardTitle;
        this.boardPlace = boardPlace;
        this.boardTag = boardTag;
        this.boardContent = boardContent;
        this.boardTime = boardTime;
    }

    public static Board toEntity(BoardReqDto boardReqDto) {
        return Board.builder()
                .boardTitle(boardReqDto.boardTitle())
                .boardTime(boardReqDto.boardTime())
                .boardPlace(boardReqDto.boardPlace())
                .boardTag(boardReqDto.boardTag())
                .boardContent(boardReqDto.boardContent())
                .build();
    }
}

보시다시피 Board라는 객체는 생성자를 가질 것이다.

모든 필드를 가지는 생성자를 작성한다. 하지만 위의 코드를 기준으로 place가 필요 없거나, tag 등 일부 필드가 필요 없다면 어떻게 생성자를 만들까?

 

place가 필요 없는 경우

public Board(String boardTitle, int boardTag, String boardContent, String boardTime) {
    this.boardTitle = boardTitle
    this.boardTag = boardTag;
    this.boardContent = boardContent;
    this.boardTime = boardTime;
}

place와 tag가 필요 없는 경우

public Board(String boardTitle, String boardContent, String boardTime) {
    this.boardTitle = boardTitle
    this.boardContent = boardContent;
    this.boardTime = boardTime;
}

이렇게 생성자를 오버로딩하는 방식을 사용할 것이다.

하지만 이러한 방식은 인스턴스 필드들이 많으면 많을수록 생성자에 들어갈 인자의 수가 늘어나 몇 번째 인자가 어떤 필드였는지 헷갈리는 경우가 생기게 된다.

 


위와 같은 경우 말고도 다양한 이유가 있다.

 

추가적으로 필드 값을 설정하기 위해 Setter를 사용할 수 있지만, 이는 불변성이 유지가 안되기 때문에 Setter 사용은 지양한다.


 

빌더를 사용하여 객체를 만들어 주기 위해서는 @Builder 어노테이션이 필요하다.

@Builder
public class Board {
	.
	.
	.
}

위와같이 클래스 상단에 붙여도 되고, 

 

아래와 같이 생성자에 붙여도 된다.

@Builder
private Board(String boardTitle, String boardPlace, int boardTag, String boardContent, String boardTime) {
    this.boardTitle = boardTitle;
    this.boardPlace = boardPlace;
    this.boardTag = boardTag;
    this.boardContent = boardContent;
    this.boardTime = boardTime;
}

여기서 접근지정자는 pubilc과 private 모두 사용 가능한데.

public은 생성자 방식과 빌더 방식 두 가지로 객체 생성이 가능하니 더 많은 유연성을 제공한다.

private은 빌더 방식으로만 객체가 생성가능 하기 때문에, 불변성이 보장된다.

 

빌더 사용 예시

public static void main(String[] args) {
	// 생성자 방식
	Board boad1 = new Board(~~~~~~~~~~~~~~);
	
  // 빌더 방식
	Board board2 = Board.builder()
                .boardTitle(~~~)
                .boardPlace(~~~)
                .boardTag(~~~)
                .boardContent(~~~)
                .boardTime(~~~)
                .build();
}

이렇게 사용한다.


 

빌더 패턴 사용 이유

  • 가독성 향상 : 많은 매개변수를 가진 생성자나 정적 팩토리 메서드를 사용하는 대신, 코드를 읽기 쉽게 만들 수 있다.
  • 유연성 : 객체를 생성할 때 필요한 매개변수를 순서에 상관없이 설정할 수 있다. 또한, 필요한 매개변수만 설정하고, 나머지 매개 변수는 기본 값이나 선택적으로 값을 설정할 수 있다.
  • 불변성 유지 : 모든 필드를 final로 선언하고, 객체 생성 후에는 변경할 수 없는 불변 객체를 만들 수 있다. 이는 다중 스레드 환경에서 객체의 안정성을 보장하며, 버그 발생 가능성을 줄인다.
  • 객체 생성 과정의 로직 분리 : 객체를 생성하는 데 필요한 복잡한 로직이 있다면, 이 로직을 빌더 클래스에 구현함으로써 객체 클래스를 보다 깔끔하게 유지할 수 있다.

끝입니다.