[Effective Java] 생성자에 매개변수가 많다면 빌더를 고려하라

두별·2023년 4월 9일
0

TIL

목록 보기
39/46

Effective Java 3/E 북스터디 기록
아이템2. 생성자에 매개변수가 많다면 빌더를 고려하라

점층적 생성자 패턴

필수 매개변수만 받는 생성자부터 시작하여 필수 매개변수와 선택 매개변수 1개를 받는 생성자, 선택 매개변수를 2개 받는 생성자 ... 선택 매개변수를 전부 다 받는 생성자까지 모든 생성자를 작성하는 방식이다.

단점

  • 가독성이 매우 매우 떨어짐. 같은 타입이 연속적으로 나열된 생성자 생성시 헷갈릴 수 있음
  • 매개변수가 많을때 원하지 않는 파라미터까지 값을 전달해줘야 할 때가 있다.

자바 빈즈 패턴

자바빈즈 패턴은 선택적 매개변수가 많을 때 활용할 수 있는 두번째 대안이다.
매개변수가 없는 생성자로 객체를 만들고 세터 (Setter) 메서드들을 호출하여 매개변수를 설정한다.

단점

  • 일관성 문제 - 필수 파라미터가 어떤 것인지 코드 작성시 알 수 없어 실수가 발생하기 쉬움.
  • 불변성 문제 - 불변 객체로 사용할 수가 없어서 개발자가 수동으로 Freezing 작업을 해주어야하는데 잘 쓰이는 방법이 아님.

빌더 패턴

빌더패턴은 점층적 생성자 패턴과 자바빈즈 패턴 두 가지의 장점만을 취한 형태이다.
빌더라는 별도의 객체를 통해 일련의 과정을 거쳐 객체를 생성하게 된다.
클래스 사용자는 직접 객체를 생성하지 않고, 필수 매개변수만으로 빌더 객체를 생성하고, 이 빌더 객체가 제공하는 일종의 세터 메소드를 사용하여 객체를 완성해갈 수 있다.
이때, 빌더 객체의 세터 메소드의 반환값은 빌더 자기 자신이므로 메소드를 연결하여 연속으로 사용하는 메소드 체이닝(Method Chaining)을 할 수 있다.

class Hamburger {
    private final int size;
    private final int bun;
    private final int patty;
    private final int lettuce;
    private final int tomato;

    private Hamburger(Builder builder) {
        this.size = builder.size;
        this.bun = builder.bun;
        this.patty = builder.patty;
        this.lettuce = builder.lettuce;
        this.tomato = builder.tomato;
    }

    public static class Builder {
        // 필수 매개변수
        private final int size;

        // 선택 매개변수
        private int bun = 0;
        private int patty = 0;
        private int lettuce = 0;
        private int tomato = 0;

        public Builder(int size) {
            this.size = size;
        }

        public Builder bun(int bun) {
            this.bun = bun;
            return this;
        }

        public Builder patty(int patty) {
            this.patty = patty;
            return this;
        }

        public Builder lettuce(int lettuce) {
            this.lettuce = lettuce;
            return this;
        }

        public Builder tomato(int tomato) {
            this.tomato = tomato;
            return this;
        }

        public Hamburger build() {
            return new Hamburger(this);
        }
    }
}

Hamburger 클래스 내부에 정적 클래스인 Builder 가 정의되어 있는 모습을 볼 수 있다. Builder 클래스는 필수 매개변수인 size 만을 전달받아 생성된다. 그리고 bun, patty, lettuce, tomato 와 같은 메소드는 빌더 자기자신의 매개변수를 설정하고, 자기자신을 반환하는 것을 확인할 수 있다.

마지막으로 build 메소드는 Hamburger 객체를 생성하여 반환하는데, Hamburger 의 매개변수에는 빌더 하나만 전달한다. Hamburger 클래스의 생성자는 Builder 를 전달받아 매개변수를 설정할 수 있다.

위 클래스는 아래와 같이 사용할 수 있을 것 이다.

Hamburger hamburger = new Hamburger.Builder(10)
    .bun(2)
    .lettuce(3)
    .patty(3)
    .build();

빌더 패턴의 단점

빌더 패턴도 장점만 존재하지는 않다. 선택적 매개변수를 많이 받는 객체를 생성하기 위해서는 먼저 빌더 클래스부터 정의해야한다. 빌더의 생성비용이 크지는 않지만, 성능에 민감한 상황에서는 문제가 될 수 있다고 한다.

또한 매개변수가 4개보다 적다면, 점층적 생성자 패턴을 사용하는 것이 더 좋다고 한다. 빌더 패턴의 코드가 다소 장황하기 때문이다. 하지만, API 는 시간이 지날수록 많은 매개변수를 갖는 경향이 있다. 따라서 애초에 빌더 패턴으로 시작하는 편이 나을 때가 많다.

리뷰

  • 일주일만에 탈주자 1명이 생겼다. 새로운 멤버를 충원해야겠다..
  • 이번주 분량 중 아이템2이 제일 길었는데 불구하고 가장 인기가 많았다.
  • 빌더는 항상 어노테이션으로만 사용해서 코드가 어떤 형태를 가지고 있는지는 몰랐는데 빌더의 동작원리를 이해할 수 있었다.

0개의 댓글