안녕하세요.

오늘은 Effective Java 에 소개 된 빌더 패턴에 대해 정리해보겠습니다.

 

 

점층적 생성자 패턴

 

사용자의 정보를 이용한 프로그램을 만든다고 가정 해보자.

요구되는 사용자의 정보는 시퀀스(PK), 이메일, 패스워드,계정 생성일자 등등..일 것이다.

 

이러한 정보를 담은 사용자 Entity는

PK와 이메일을 가져오는 사용자 목록 조회성 화면이나 이메일, 패스워드를 입력받는 가입 화면 등에 사용된다.

 

이처럼 서비스마다 사용하는 매개변수가 다르기때문에

개발자는 아래와 같이 매개변수를 늘려가며 생성자를 만들게 된다.

 

public class UserSample {
    private Long seq;
    private String email;
    private String passwd;
    private String name;
    private LocalDateTime createAt;

    public UserSample(Long seq, String email) {
        this.seq = seq;
        this.email = email;
    }

    public UserSample(Long seq, String email, String passwd) {
        this.seq = seq;
        this.email = email;
        this.passwd = passwd;
    }

    public UserSample(Long seq, String email, String passwd, String name) {
        this.seq = seq;
        this.email = email;
        this.passwd = passwd;
        this.name = name;
    }

    public UserSample(Long seq, String email, String passwd, String name, LocalDateTime createAt) {
        this.seq = seq;
        this.email = email;
        this.passwd = passwd;
        this.name = name;
        this.createAt = createAt;
    }
}

 

점층적 생성자 패턴을 사용하는 경우, 변수가 많아질수록 가독성이 떨어지고

초기화하길 원하는 일부 변수를 위해 원치않는 다른 변수까지 초기화해야하는 상황을 마주할 수 있다.

 

 

 

자바 빈 패턴

 

파라미터가 없는 생성자로 객체를 생성하고, 이후에 setter를 사용해서 값을 초기화한다.

 

public class UserSample {
    private Long seq;
    private String email;
    private String passwd;
    private String name;
    private LocalDateTime createAt;

    public UserSample() {}

    public void setSeq(Long seq) {
        this.seq = seq;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setCreateAt(LocalDateTime createAt) {
        this.createAt = createAt;
    }
}
UserSample userSample = new UserSample();
userSample.setSeq(1L);
userSample.setName("una");

 

자바 빈 패턴을 사용하면 가독성도 좋고 변수를 선택적으로 초기화할 수 있다.

하지만 객체 하나를 완전하게 만들기 위해서 메소드를 여러개 호출해야해서 객체의 일관성이 일시적으로 깨지게 된다.

 

즉, 자바 빈 패턴은 스레드 안정성이 없고 불변 클래스(Immutable class)를 만들 수 없다는 단점이 있다.

 

 

 

빌더 패턴으로 불변 클래스 만들기

 

이펙티브 자바에 의하면

클래스는 변경해야하는 이유가 없다면 최대한 immutable하게 만들어야한다.

 

초기화하는 내용을 보고 가져다 썼는데 중간 어디선가 값이 변경된다면..

컴파일 시에 오류를 잡을 수 없고 런타임에 엉뚱한 결과를 보고 집에 가고싶어지는 것을 방지하기 위해서이지 않을까

+ 스레드 안정성

 

불변 클래스란 한 번 생성되면 상태를 변경하지 않는 객체를 의미하는데

이는 모든 값은 객체가 생성될 때 초기화되고, 객체의 수명이 끝날때까지 변경되지 않는다는 뜻이다.

 

아래와 같이 불변 객체를 만들 수 있다.

 

public class UserSample {
    private final Long seq;
    private final String email;
    private final String passwd;
    private final String name;
    private final LocalDateTime createAt;

    private UserSample(Builder builder) {
        this.seq = builder.seq;
        this.email = builder.email;
        this.passwd = builder.passwd;
        this.name = builder.name;
        this.createAt = builder.createAt;
    }

    public static class Builder {
        // 필수
        private final Long seq;
        private final String email;

        // 선택
        private String passwd = "";
        private String name = "";
        private LocalDateTime createAt = null;

        // 필수 매개변수 초기화
        public Builder(Long seq, String email) {
            this.seq = seq;
            this.email = email;
        }

        public Builder passwd(String pw) {
            this.passwd = pw;
            return this;
        }

        public Builder name(String name) {
            this.passwd = name;
            return this;
        }

        public Builder createAt(LocalDateTime createAt) {
            this.createAt = createAt;
            return this;
        }

        // 불변 객체 생성
        public UserSample build() {
            return new UserSample(this);
        }
    }
}
UserSample userSample = new UserSample.Builder((1L, "email").passwd("pw").name("name").build();

 

빌더 패턴은 객체를 생성하기 전에 setter를 사용해 값을 초기화한다.

setter method는 Builder 객체(this)를 리턴하기때문에 여러 메소드를 순차적으로 선언할 수 있다.

이를 메소드 체이닝이라고 한다.

 

변수를 전부 초기화하고 마지막에 build method를 호출하여 객체를 생성하므로

일관성이 유지되고 thread-safe한 불변 객체를 얻을 수 있는 것이다!

 

 

복사했습니다!