티스토리 뷰

반응형

객체를 생성하기 위해서는 생성자 패턴, 정적 메소드 패턴, 수정자 패턴, 빌더 패턴 등을 사용할 수 있습니다. 개인적으로 객체를 생성할 때에는 반드시 빌더 패턴을 사용해야 한다고 생각하는데, 왜 빌더 패턴을 사용해야 하는지에 대해 포스팅해보고자 합니다.

 

 

1. 빌더 패턴(Builder Pattern)을 사용해야 하는 이유


생성자와 수정자로 구현된 다음과 같은 User 클래스를 바탕으로 왜 생성자나 수정자보다 빌더를 써야하는지 이해해보도록 하자.

@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String name;
    private int age;
    private int height;
    private int iq;

}

 

 

[ 빌더 패턴(Builder Pattern)의 장점 ]

 

  1. 필요한 데이터만 설정할 수 있음
  2. 유연성을 확보할 수 있음
  3. 가독성을 높일 수 있음
  4. 불변성을 확보할 수 있음

 

1. 필요한 데이터만 설정할 수 있음

예를 들어 User 객체를 생성해야 하는데 age라는 파라미터가 필요 없는 상황이라고 가정하자.

생성자나 정적 메소드를 이용하는 경우라면 우리는 age에 더미 값을 넣어주거나 age가 없는 생성자를 새로 만들어주어야 한다.

// 1. 더미값을 넣어주는 방법
User user = new User("망나니개발자", 0, 180, 150)

// 2. 생성자 또는 정적 메소드를 추가하는 방법
@NoArgsConstructor 
@AllArgsConstructor 
public class User { 
    private String name;
    private int age;
    private int height;
    private int iq;

    public User (String name, int height, int iq) {
        this.name = name;
        this.height = height;
        this.iq = iq;
    }
    
    public User static of(String name, int height, int iq) {
        return new User(name, 0, 180, 150);
    }
    
}

 

이러한 작업이 한번 두번이면 괜찮지만 반복적인 경우에는 시간 낭비로 이어지게 된다. 하지만 빌더를 이용하면 동적으로 이를 처리할 수 있다.

User user = User.builder()
             .name("망나니 개발자")
             .height(180)
             .iq(150).build();

 

그리고 이렇게 필요한 데이터만 설정할 수 있는 빌더의 장점은 생성자 또는 정적 메소드와 비교하여 테스트용 객체를 생성할 때 용이하게 해주고, 불필요한 코드의 양을 줄이는 등의 이점을 안겨준다.

 

 

2. 유연성을 확보할 수 있음

예를 들어 User 클래스에 몸무게를 나타내는 새로운 변수 weight를 추가해야 한다고 하자. 하지만 이미 다음과 같이 생성자로 객체를 만드는 코드가 있다고 하자.

// ASIS
User user = new User("망나니개발자", 28, 180, 150)

// TOBE
User user = new User("망나니개발자", 28, 180, 150, 75)

 

그러면 우리는 새롭게 추가되는 변수 때문에 기존의 코드를 수정해야 하는 상황에 직면하게 된다. 기존에 작성된 코드의 양이 방대하다면 감당하기 어려울 수 있다. 하지만 빌더 패턴을 이용하면 새로운 변수가 추가되는 등의 상황에 직면하여도 기존의 코드에 영향을 주지 않을 수 있다.

 

@Test
public void 1번테스트() {
 
    // 수정 필요함 (ASIS)
    User user = new User("망나니개발자", 28, 180, 150);
    
    // 수정 필요함 (TOBE)
    User user = new User("망나니개발자", 28, 180, 150, 75);

    ...

}

... 

@Test
public void 100번테스트() {

    // 수정 필요함 (ASIS)
    User user = new User("망나니개발자", 28, 180, 150);
    
    // 수정 필요함 (TOBE)
    User user = new User("망나니개발자", 28, 180, 150, 75);

    ...

}

 

만약 위와 같이 User 객체를 생성하는 코드가 100개 있다면 모든 로직을 수정해주거나 생성자를 따로 추가하는 등의 불필요한 조치를 해주어야 할 것이다. 하지만 빌더 패턴를 기반으로 코드가 작성되어 있다면 기존의 코드는 수정할 필요가 없다. 왜냐하면 빌더 패턴은 유연하게 객체의 값을 설정할 수 있도록 도와주기 때문이다.

 

 

3. 가독성을 높일 수 있음

빌더 패턴을 사용하면 매개변수가 많아져도 가독성을 높일 수 있다. 생성자로 객체를 생성하는 경우에는 매개변수가 많아질수록 코드 리딩이 급격하게 떨어진다. 예를 들어 다음과 같은 코드가 있다고 하자.

User user = new User("망나니개발자", 28, 180, 150)

 

위와 같은 코드를 보면 28와 180 또는 150이 무엇을 의미하는지 바로 파악이 힘들고, 클래스 변수가 4개 이상만 되어도 코드를 읽기 힘들어진다. 하지만 다음과 같이 빌더 패턴을 적용하면 가독성을 높일 수 있고, 직관적으로 어떤 데이터에 어떤 값이 설정되는지 쉽게 파악할 수 있다.

User user = User.builder()
             .name("망나니 개발자")
             .age(28)
             .height(180)
             .iq(150).build();

 

 

4. 불변성을 확보할 수 있음

많은 개발자들이 수정자 패턴(Setter)를 흔히 사용한다. 하지만 Setter를 구현한다는 것은 불필요하게 확장 가능성을 열어두는 것이다. 이는 Open-Closed 법칙에 위배되고, 불필요한 코드 리딩 등을 유발한다. 그렇기 때문에 클래스 변수를 final로 선언하고 객체의 생성은 빌더에 맡기는 것이 좋다. (불변성을 확보해야 하는 이유에 대해 이해가 부족하다면 이 포스팅을 참고해주세요)

 

위의 User 클래스를 다음과 같이 수정할 수 있다.

@RequiredArgsConstructor
@Builder
public class User {

    private final String name;
    private final int age;
    private final int height;
    private final int iq;

}

 

 

다른 사람과 협업을 하거나 변경에 대한 요구 사항이 많은 경우라면 빌더 패턴을 사용해야 하는 이유를 더욱 극명하게 느낄 수 있다. 만약 아직 객체의 생성을 위해 생성자나 수정자 패턴 또는 정적 메소드 패턴을 사용하고 있다면 롬복을 사용해 빌더 패턴을 적용하도록 하자.

객체를 생성하는 대부분의 경우에는 빌더 패턴을 적용하는 것이 좋다. 물론 예외적인 케이스로 엔티티(Entity) 객체 또는 도메인(Domain) 객체로부터 DTO를 생성하는 경우라면 직접 빌더를 만들고 하는 작업이 번거로우므로 MapStruct나 Model Mapper와 같은 라이브러리를 통해 생성을 위임할 수 있다.

 

 

혹시 제가 놓쳤거나 추가할만한 빌더 패턴의 장점이 있다면 댓글 남겨주세요! 업데이트 하도록 하겠습니다:)

반응형
댓글
댓글쓰기 폼
  • 1234 빌더 패턴의 장점은 한번 세팅하고 변경없이 그 값을 유지한다고 알고 있어요. 그럼 고정 세팅이 아니고 변경 될수 있다면 빌더 패턴이 효율이 떨어지지 않을까요?
    상황에 맞게 빌터 패턴을 (사용할시 말지 ) 선택할수 있는 방법을 가르쳐 주시면
    초보자 입장에서는 더욱 도움이 될듯 합니다.

    2021.09.06 13:17
  • 망나니개발자 안녕하세요~ 방문해주셔서 감사합니다!
    한번 세팅하고 변경없이 그 값을 유지하는 것은 빌더패턴의 장점이 아닙니다! 그 장점은 개방 폐쇄의 법칙 또는 불변성으로 얻을 수 있는 장점이고, 빌더와 결합이 가능할 뿐 입니다.
    위에서 설명하였듯 빌더 패터의 장점은 객체 생성에 필요한 데이터를 유연하게 설정할 수 있고, 그에 대한 가독성을 높이는 등이 있으며 본문에서 설명하였듯 객체를 생성하는 대부분의 모든 경우에는 빌더를 사용하는게 좋다고 생각합니다. 물론 DTO의 생성에는 modelMapper와 같은 라이브러리를 사용할 수도 있습니다.
    => 이와 관련된 내용은 본문에 추가하였습니다:)
    2021.09.06 14:06 신고
  • 닌텐도스위치 매니아 modelMapper는 리플렉션 때문에 성능 저하가 심하니 mapstruct를 사용해주세요. 2021.09.29 19:40 신고
  • 망나니개발자 ModelMapper가 변수에 값을 할당하기 위해 내부적으로 Reflection을 사용하는데, 이와 관련해서 성능 이슈에 대한 얘기가 많이 있고, 논란이 많이 되었죠ㅎㅎ
    위에서 의도한 내용은 ModelMapper에 대해서 얘기하고자 하는게 아니라, 변환을 위한 라이브러리들이 있다는 얘기입니다:)
    2021.09.29 20:18 신고
  • superpil 안녕하세요. 좋은 글 감사합니다~
    Builder패턴 장점 중에서 1번에 궁금한점이 있어 댓글 남깁니다.
    필요한 데이터만 설정할수있다고 말씀하셨는데 builer패턴이 아닌 getter, setter로 객체를 생성해도 필요한 데이터만 setter로 설정할 수 있지 않나욥!?
    2021.10.09 16:21
  • 망나니개발자 안녕하세요~ 먼저 방문해주셔서 감사합니다!
    1번의 장점이 비교되는 대상은 Getter,Setter 보다는 생성자 또는 정적 메소드 패턴을 이용하는 경우가 될 것 같습니다ㅎㅎ
    제가 비교되는 대상을 정확하게 적어두지 않았는데(본문에 해당 내용 반영해둘께요!) 말씀해주신대로 Getter,Setter로 원하는 데이터만을 설정할 수 있지만, 4번의 문제는 해결하기 어렵습니다:) 하지만 4번의 불변성을 지켜주는 장점이, 애플리케이션을 유지보수하고 개발하는데 도움이 많이 된다고 생각합니다ㅎㅎ
    2021.10.09 16:35 신고
  • superpil 아 그렇군요! 답변 감사합니다!! 답변 참고해서 공부하겠습니다~! 2021.10.09 23:41
  • 망나니개발자 공부하시다가 또 궁금한부분 생기면 편하게 댓글 남겨주세요:) 2021.10.09 23:57 신고
반응형
공지사항
Total
1,971,101
Today
159
Yesterday
1,908
TAG more
«   2022/01   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
글 보관함