본문 바로가기

Back-End 공부/Spring

생성자 객체 생성 방식의 단점을 극복한 builder 패턴

🎈 생성자 방식

1. 매개변수 개수의 유연성이 떨어진다.

-> name, email만 사용해서 Author 객체를 생성하고 싶으면 생성자를 새로 만들어주어야 한다.

컬럼의 개수가 많을 경우에는 상당히 복잡하고 불필요한 작업이 된다.

 

2. 매개변수의 순서의 유연성이 떨어진다.

매개변수가 적혀있는 순서대로 값을 넣어주지 않으면, 원하는 객체를 생성할 수 없다.

public Author(String name, String email, String password, Role role){
    this.name = name;
    this.email = email;
    this.password = password;
    this.role = role;
}

 

 

 

🎈 빌더패턴

매개변수의 순서, 개수 등을 유연하게 세팅

 

[ Author.java ]

@Getter
@Entity
@NoArgsConstructor
@Builder
// Builder 어노테이션을 통해 빌더 패턴으로 객체 생성
// 매개변수의 순서, 개수 등을 유연하게 세팅
@AllArgsConstructor
//위와 같이 모든 매개변수가 있는 생성자를 생성하는 어노테이션과 Builder를 클래스에 붙여
//모든 매개변수가 있는 생성자 위에 Builder 어노테이션과 붙인 것과 같은 효과가 있음.
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 20)
    private String name;

    @Column(nullable = false, length = 20, unique = true)
    private String email;

    @Column(nullable = false)
    private String password;

    @Enumerated(EnumType.STRING)
    private Role role;

    @CreationTimestamp
    // 개발자가 DB를 바꾸는 게 risky한 것. 프로그램적으로 다루는 것이 좋다.
    @Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    LocalDateTime createdTime;

    @UpdateTimestamp
    @Column(columnDefinition = "TIMESTAMP ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    LocalDateTime updatedTime;


//    public Author(String name, String email, String password, Role role){
//        this.name = name;
//        this.email = email;
//        this.password = password;
//        this.role = role;
//    }

    public void updateAuthor(String name, String password){
        this.name = name;
        this.password = password;
    }
}

 

 

[ AuthorService.java ]

Author author = Author.builder()
        .email(authorSaveReqDto.getEmail())
        .name(authorSaveReqDto.getName())
        .password(authorSaveReqDto.getPassword())
        .build();

 

".속성명" 이라는 메서드를 만들어

호출이 되면 해당 속성에 값이 세팅이 되고,

호출이 안되면 해당 속성은 null 값을 가지고 있어 객체를 생성할 때 해당 속성을 갖지 않게 되는 것이다.

 

 

내부적으로 모든 속성에 대해 null 값을 세팅하고, 매개변수로 넘어온 값들만 넣어 객체를 생성한다.

사실, 위의 생성자 방식도 이런 식으로 코드를 구현한다면 매개변수 개수에 관계없이 유연하게 객체를 만들 수 있다.

Author author = new Author(authorSaveReqDto.getName(),
                            authorSaveReqDto.getEmail(),
                            authorSaveReqDto.getPassword(),
                            "");

 

 

 

 

그렇다면 순서가 바뀌는 건 어떻게 처리할까?

매개변수에 집어넣는 속성 값의 순서가 바뀌어도 올바른 객체 생성을 보장하는 이유는 뭘까? 

 

직접 구현하면서 알아보자

 

 

Builder 패턴 직접 구현하기

 

[ Hello.java ]

@Data
public class Hello{
    private String name;
    private String email;
    private String password;

    //Builder 패턴 직접 구현
    public Hello(MyBuilder myBuilder) {
        this.name = myBuilder.name;
        this.email = myBuilder.email;
        this.password = myBuilder.password;
    }

    public static MyBuilder builder(){
        return new MyBuilder();
    }

    public static class MyBuilder{
        private String name;
        private String email;
        private String password;
        public MyBuilder name(String name){
            this.name = name;
            return this;
        }
        public MyBuilder email(String email){
            this.email = email;
            return this;
        }
        public MyBuilder password(String password){
            this.password = password;
            return this;
        }
        public Hello build(){
            return new Hello(this);
        }
    }
}

 

 

[ HelloController.java ]

public void helloBuilderTest(){
    Hello hello = Hello.builder()
            .name("hongildong")
            .email("hongildong@naver.com")
            .password("1234")
            .build();
}

 

builder() 메서드가 호출되면서 MyBuilder 객체를 생성

 -> 속성마다 .name .email .password 메서드들을 정의하고, 순서에 상관없이 들어오는대로 속성 값 설정

-> 이때, 호출이 안되는 ".속성" 메서드는 값이 세팅이 되지 않기 떄문에 null 값을 가짐

-> 변수 개수에 따라 선택적으로 값이 삽입된 MyBuilder 객체를 Hello 객체에 넣어주면서 hello 생성

 

 

 

위의 Author.java 코드에서는 @Builder와 @AllargsConstructer 어노테이션을 통해

이런 복잡한 코드를 구현하지 않고 사용할 수 있다.

 

@AllargsConstructer로 모든 속성에 대한 생성자를 만들어 놓고,

추후에 객체를 생성하고 싶을 때마다 원하는 속성 값만 메서드 형태로 넘기면 된다.

 

두 어노테이션은 세트로 붙어다녀야 한다는 것을 잊지 말자.