ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인 패턴] Builder pattern
    Backend/공부,개념 2021. 8. 25. 01:25
    반응형

    빌더 패턴 (Builder pattern)

    생성자에 인자가 많을 때는 빌더 패턴을 고려하라 - 이펙티브 자바 #2

    null

    기본적인 생성자 생성 방식은, 모든 파라미터를 포함하는 생성자를 만들고, 객체 생성 시 사용하지 않는 파라미터에는 null을 넣었다. 이런 생성자 생성방식에는 가독성, null에 대한 오류 문제 등이 발생한다.

    점층적 생성자 패턴

    1개의 생성자가 아닌, 여러 파라미터가 들어오는 경우를 모두 오버로딩 하여 생성자를 생성할 수도 있다. 하지만 이렇게 되면 같은 타입의 파라미터 순서를 변경했을 경우 알아차리지 못하는 문제 등이 발생한다.

    java bean 패턴, setter 사용

    기본 생성자를 만들고 set을 통해 값을 세팅해주는 방법이 있다. 하지만 이 패턴은 좋지 않아보인다.

     

    생성자로 초기화가 이루어 지지도 않고, 데이터에 set을 통한 접근을 허용하게된다. 1회의 호출로 객체 생성이 끝나지 않고 계속 값을 생성해주어 객체 일관성이 깨진다. 그리고 변경불가능한 immutable class를 만들 수도 없다.

    빌더 패턴

    빌더 패턴(Builder pattern)이란 복잡한 객체의 생성 과정과 표현 방법을 정의하는 클래스를 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴이다.

    위키에 정의된 글인데 너무 어려운 말인 것 같다.


    주어진 상황은 클래스가 가지고있는 변수가 많고, 이를 모두 포함하는 생성자를 만들어내는 과정이 복잡한 상황이다. 객채 생성 (xx) / 생성자 생성(xxBuilder) 을 분리하고, 객체 = new xxbuilder().param1().param2()...build() 이런 과정을 통해 필수적으로 생성해야 하는 변수와, 체이닝을 통한 선택적인 생성이 가능해서 동일한 절차에 서로 다른 결과를 만들어 낸다고 적혀있는것 같다. 일단 아래 내용을 적으면서 이해해본 바로 나 스스로는 이렇게 정리해보려고 한다.

     

    빌더 패턴을 이용하면 앞서 나온 방식(점층적 생성자, bean) 의 문제를 해결해줄 수 있다.

     

    • 빌더 패턴을 활용하면 어떤 필드에 어떤 인자를 넣어줬는지 명확히 알 수 있고, 넣어줄 필요가 없는 필드는 굳이 선언 할 필요가 없다.
    • 무조건적인 setter 생성을 방지해 불변 객체로 만들 수 있다.
    • 필수 argument를 지정할 수 있다.

     

        class Person{
            private final String name;
            private final String age;
            private final String address;
            private final String hobby;
    
            public Person(Builder builder) {
                this.name = builder.name;
                  this.age = builder.age;
                  this.address = builder.address;
                  this.hobby = builder.hobby;
            }
        }
    
       class Builder {
         // 필수 값
            private String name;
                 private String age;
         // 선택 값(default값 세팅 )
                 private String address="";
                 private String hobby="";
    
                 public Builder(String name, String age){
                  this.name = name;
                  this.age = age;
            }
        // chaining
                 public Builder address(String address){
                  this.address = address;
                  return this; // .으로 체이닝 가능
            }
                 public Builder hobby(String hobby){
                  this.hobby = hobby;
                  return this;
            }
    
          // build() 함수 
            public Person build() {
                return new Person(this);
            }
        }
    
    // 사용법
    Person person = new Person.builder("js", "25")
                                      .address("korea")
                                      .hobby("coding")
                                      .build();

    다음의 코드를 보면 생성자의 파라미터가 Builder이고 Builder 객체의build() 함수를 통해서 Person객체를 생성한다.

    Builder 내부에서 생성자로 필수값 생성 후 선택적으로 값을 세팅 하고 자기 객체(this)를 반환하여 체이닝 방식으로 값을 지정하게 해준다.

    이를 Lombok 라이브러리의 @Builder 어노테이션이 해결해준다.

    출처

     


    [참고] NoArgsConstructor

    기본생성자는 자동으로 생성되는데, 만약 매개변수를 가지는 생성자를 하나라도 정의했다면, 기본 생성자는 자동으로 추가되지 않는다.

    Builder를 통해 생성자를 만들기 때문에, 따라서 기본 생성자 생성이 필요하다. 이때 @NoArgsConstructor를 사용하면 lombok이 기본 생성자를 생성해준다.

     

    class바깥에 @Builder와 @NoArgsConstructor를 같이 사용하면 컴파일 에러가 발생한다. Builder는 생성자가 없는 경우 모든 멤버변수를 파라미터로 받는 기본 생성자를 생성하고, 생성자가 있을 경우 따로 생성자를 생성하지 않기 때문이다.

    따라서 클래스가 아닌 생성자에 @Builder를 추가하면 된다. 그러면 의미있는 객체만 생성할 수 있게 된다.

    출처

    댓글

Designed by Tistory.