[java] 효과적인 Java의 빌더 패턴

나는 최근 Joshua Bloch의 Effective Java를 읽기 시작했습니다. 빌더 패턴 [책의 항목 2]에 대한 아이디어가 정말 흥미로 웠습니다. 내 프로젝트에서 구현하려고 시도했지만 컴파일 오류가 발생했습니다. 본질적으로 내가하려고했던 것은 다음과 같습니다.

여러 속성이있는 클래스 및 해당 빌더 클래스 :

public class NutritionalFacts {
    private int sodium;
    private int fat;
    private int carbo;

    public class Builder {
        private int sodium;
        private int fat;
        private int carbo;

        public Builder(int s) {
            this.sodium = s;
        }

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

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

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

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}

위의 클래스를 사용하려고하는 클래스 :

public class Main {
    public static void main(String args[]) {
        NutritionalFacts n =
            new NutritionalFacts.Builder(10).carbo(23).fat(1).build();
    }
}

다음과 같은 컴파일러 오류가 발생합니다.

효과적인 java.BuilderPattern.NutritionalFacts.Builder를 포함하는 둘러싸는 인스턴스가 필요합니다 NutritionalFacts n = new NutritionalFacts.Builder (10) .carbo (23) .fat (1) .build ();

메시지의 의미를 이해하지 못합니다. 설명 해주십시오. 위 코드는 그의 책에서 Bloch가 제안한 예제와 유사합니다.



답변

빌더를 static클래스로 만드십시오 . 그런 다음 작동합니다. 정적이 아닌 경우 소유 클래스의 인스턴스가 필요하며 인스턴스가 없어야하며 빌더없이 인스턴스를 작성하는 것도 금지되어야합니다.

public class NutritionFacts {
    public static class Builder {
    }
}

참조 : 중첩 클래스


답변

Builder 클래스를 정적으로 만들어야하고 필드를 최종으로 설정하고 해당 값을 가져 오기위한 getter가 있어야합니다. 해당 값에 세터를 제공하지 마십시오. 이런 식으로 수업은 불변이 될 것입니다.

public class NutritionalFacts {
    private final int sodium;
    private final int fat;
    private final int carbo;

    public int getSodium(){
        return sodium;
    }

    public int getFat(){
        return fat;
    }

    public int getCarbo(){
        return carbo;
    }

    public static class Builder {
        private int sodium;
        private int fat;
        private int carbo;

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

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

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

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

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}

이제 다음과 같이 속성을 설정할 수 있습니다.

NutritionalFacts n = new NutritionalFacts.Builder().sodium(10).carbo(15).
fat(5).build();


답변

정적이 아닌 클래스에 정적 방식으로 액세스하려고합니다. 로 변경 Builder하면 static class Builder작동합니다.

Builder존재하는 인스턴스가 없으므로 사용 예제가 실패합니다 . 모든 실용적인 목적을위한 정적 클래스는 항상 인스턴스화됩니다. 정적으로 만들지 않으면 다음과 같이 말해야합니다.

Widget = new Widget.Builder(10).setparm1(1).setparm2(3).build();

Builder매번 새로운 것을 만들어야하기 때문입니다.


답변

Intellij IDEA에서 내부 빌더를 생성하려면 다음 플러그인을 확인하십시오. https://github.com/analytically/innerbuilder


답변

Builder내부 클래스를로 선언해야합니다 static.

비 정적 내부 클래스정적 내부 클래스에 대한 설명서를 참조하십시오 .

기본적으로 정적이 아닌 내부 클래스 인스턴스는 연결된 외부 클래스 인스턴스가 없으면 존재할 수 없습니다.


답변

일단 아이디어가 생기면 실제로는 롬복이 @Builder훨씬 더 편리하다는 것을 알게 될 것 입니다.

@Builder 다음과 같은 코드로 클래스를 인스턴스화하는 데 필요한 코드를 자동으로 생성 할 수 있습니다.

Person.builder()
  .name("Adam Savage")
  .city("San Francisco")
  .job("Mythbusters")
  .job("Unchained Reaction")
 .build(); 

공식 문서 : https://www.projectlombok.org/features/Builder


답변

이것은 둘러싸 기 유형을 만들 수 없음을 의미합니다. 즉, 먼저 “부모”클래스의 인스턴스를 승인 한 다음이 인스턴스에서 중첩 된 클래스 인스턴스를 작성할 수 있습니다.

NutritionalFacts n = new NutritionalFacts()

Builder b = new n.Builder(10).carbo(23).fat(1).build();

중첩 클래스