개발/Spring&SpringBoot

빌더 패턴(Builder Pattern) 만들기 + Lombok이용

라니킴 2021. 12. 3. 17:14

이번 주 프로젝트 과제 테스트코드가 Builder로 작성되어 있어 공부해보았습니다.

 

◆ Builder pattern이란?

 생성 패턴 중 하나로 많은 인자를 가진 객체의 생성을 다른 객체의 도움으로 생성하는 패턴.

 

* 생성패턴은 인스턴스를 만드는 절차를 추상화하는 패턴이다.

 

User 클래스

@NoArgsConstructor
@AllArgsConstructor
public class User{

    private String name;
    private int age;
    private int height;
    private int weight;

}

 

객체를 만드는 대표적인 방법에는 생성자 패턴이 있다.

User user = new User("snoopy", 10, 120, 50);
User user = new User("snoopy", 10, 0, 0); // 필요없는 매개변수도 기본값으로 넣어줘야 한다.

생성자 패턴으로 객체를 생성하게 되면 인자로 어떤 값이 들어가는 지 한 눈에 알아 볼 수 없어 가독성이 떨어진다.

또한, 현재는 4개의 인자만 입력하면 되지만 인자를 많이 입력해야 한다면 순서를 맞춰 입력하고 모든 값이 입력되었는지 확인하는 과정이 아주 힘들 것이다. 

 

두 번째 방법. 자바빈 

아무런 매개변수를 받지 않는 생성자를 사용해서 인스턴스를 만들고, setter를 사용해서 필요한 필드만 설정할 수 있다.

 

User user = new User();
 user.setName("snoopy");
 user.setAge("20");
 user.setHeight("120");
 user.setWeight("50");

이 방법의 경우 가독성은 생성자패턴에 비해 좋다.

단점은 최종적인 인스턴스를 만들기까지 여러 번의 호출을 거쳐야 하기 때문에 중간에 사용되는 경우 안정적이지 않은 상태가 될 수 있다. 또한, setter를 사용하기 때문에 불변객체로 만들지 못한다. 코드양도 늘어난다.

 

생성자의 안정성과 자바빈의 가독성의 장점을 모두 취할 수 있는 대안이 빌더 패턴이다.

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

  1. 필요한 데이터만 설정할 수 있음
  2. 유연성을 확보할 수 있음
  3. 가독성을 높일 수 있음
  4. 불변성을 확보할 수 있음( sertter 메소드가 없으므로)

위의 User 클래스를 빌더패턴으로 바꾸면 아래와 같다.

public class User{
    private String name; 
    private int age;
    private int height;
    private int weight;
    
    // 생성자를 private로 작성한 이유는
    // 외부에서는 접근할 수 없고 Builder 클래스를 통해서만 객체를 생성하겠다는 의미.
    private User(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.height = builder.height;
        this.weight = builder.weight;
    }

    //Builder Class: 생성할 클래스 안에 정적 멤버 클래스로 만들어두는것이 일반적이다.
    public static class Builder {
        private final String name; //필수로 입력받을 값은 final 붙여줘야한다.
        private final int age;
        private int height;
        private int weight;

        //생성시 필수값 초기화.
        public Builder(String name, int age) {
            if (name == null || age == 0) {
                throw new IllegalArgumentException("이름과 나이는 반드시 입력해야합니다.")
            }
            
            this.name = name;
            this.age =age;
        }

        public  Builder height(int height) {
            this.height = height;
            return this; //체이닝 할 수 있도록 자신을 return 시켜줌.
        }
        
        public  Builder weight(int weight) {
            this.weight = weight;
            return this;
        }
        
		
        // 마지막에 builder() 메소드를 실행하면 this가 리턴되도록 함.
        public User build() {
            return new User(this);
        }

    }
}

Builder 사용하기

User user = User.Builder("snoopy", 12)
        .height(120)
        .weight(50)
        .build();
        
User newUser = User.Builder("soda", 20)
        .height(120)
        .build();

값을 넣고 싶은 변수만 선택적으로 객체 생성이 가능하다.

위와 같이 builder 패턴이 좋은 것은 알겠으나 클래스를 만들 때마다 builder 클래스를 작성해야하는 번거로움이 있다.

 

그래서 Lombok에서 빌터 패턴을 제공한다.

@Builder 어노테이션을 사용하면 builder 클래스를 작성하지 않아도 된다. 

 

Lombok @Builder

@Builder
public class User{
    private String name; 
    private int age;
    private int height;
    private int weight;
}

//builder 사용
User user = User.builder()
		.name("soda")
        	.age(20)
        	.height(170)
        	.weight(50)
        	.build();