java - 빌더패턴 - 이펙티브 자바 빌더 패턴




빌더 패턴 대 구성 객체 (6)

빌더 패턴은 변경 불가능한 오브젝트를 작성하는 데 일반적이지만 빌더를 작성하는 데 약간의 프로그래밍 오버 헤드가 있습니다. 그래서 단순히 config 객체를 사용하는 것이 아닌가하는 의문이 든다.

빌더 사용법은 다음과 같습니다.

Product p = Product.Builder.name("Vodka").alcohol(0.38).size(0.7).price(17.99).build();

이것은 매우 읽기 쉽고 간결하지만, 빌더를 구현해야한다는 것은 명백합니다.

public class Product {

    public final String name;
    public final float alcohol;
    public final float size;
    public final float price;

    private Product(Builder builder) {
        this.name = builder.name;
        this.alcohol = builder.alcohol;
        this.size = builder.size;
        this.price = builder.price;
    }

    public static class Builder {

        private String name;
        private float alcohol;
        private float size;
        private float price;

        // mandatory
        public static Builder name(String name) {
            Builder b = new Builder();
            b.name = name;
            return b;
        }

        public Builder alcohol(float alcohol) {
            this.alcohol = alcohol;
            return.this;
        }

        public Builder size(float size) {
            this.size = size;
            return.this;
        }

        public Builder price(float price) {
            this.price = price;
            return.this;
        }

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

    }

}

내 생각은 다음과 같은 간단한 구성 개체를 사용하여 코드를 줄이는 것입니다.

class ProductConfig {

        public String name;
        public float alcohol;
        public float size;
        public float price;

        // name is still mandatory
        public ProductConfig(String name) {
            this.name = name;
        }

}

public class Product {

    public final String name;
    public final float alcohol;
    public final float size;
    public final float price;

    public Product(ProductConfig config) {
        this.name = config.name;
        this.alcohol = config.alcohol;
        this.size = config.size;
        this.price = config.price;
    }

}

용법:

ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);

이 사용법은 몇 줄이 더 필요하지만 매우 읽기 쉽지만 구현이 훨씬 간단하며 빌더 패턴에 익숙하지 않은 사람이 이해하기 쉽습니다. 그건 그렇고 :이 패턴의 이름은 무엇입니까?

간과 한 설정 접근법에 단점이 있습니까?


IMO, 유효성 검사 등의 작업이 있으면 빌더 패턴이 훨씬 더 강력합니다.

사례의 작성자 패턴을 변경하여 다음을 수행 할 수 있습니다.

Product p = new ProductBuilder("pName").alcohol(0.38).size(0.7).price(17.99).build();

build() 메소드는 빌더에 필요한 모든 유효성 검증 작업을 수행 할 수 있습니다. 빌더 패턴에는 여러 가지 디자인 조언 (귀하의 경우에는 적용되지 않을 수도 있음)이 있습니다. deatils의 경우이 question 확인


가장 큰 단점은 조슈아의 책에없는 것이므로 드론은 머리를 감쌀 수 없다는 것입니다.

간단한 값 객체를 사용하여 함수 (/ 메소드 / 생성자)가 필요로하는 여러 인수를 보유하고 있지만, 아무런 문제가 없으며 여러 변수에 대해 수행되었습니다. 우리가 선택적 매개 변수를 명명하지 않은 한, 우리는 이것과 같은 임시 해결책을 고안해야합니다. 그것은 수치스럽고, 일요일의 신들로부터의 훌륭한 신의 발명품이 아닙니다.

실제 차이점은 필드를 직접 노출한다는 것입니다. Joshua는 공개적으로 변경 될 수있는 분야가 결코 없을 것입니다. 그러나 그는 수백만 명의 사람들이 쓸모없는 API를 작성하고, API는 수십 년 동안 진화하는 데 안전해야하며 설계에만 많은 인력을 배치 할 수 있습니다 간단한 수업

누가 그것을 흉내낼 것인가?


구성 패턴과 빌더 패턴은 기능적으로 동일합니다. 둘 다 똑같은 문제를 해결합니다.

  • 다중 생성자 서명의 필요성 제거

  • 건설 중에 필드 만 설정하도록 허용

  • 소비자가 관심을 갖는 값만 설정하고 다른 값에 대해서는 논리적 기본값을 허용 할 수 있습니다.

유효성 검사를 수행하고 캡슐화 된 논리로 상태를 설정하는 메서드로 상태를 설정할 수 있도록 허용하는 것과 같이 다른 패턴에서 수행 할 수있는 이러한 패턴 중 하나에서 수행하려는 작업. 유일한 차이점은 new 키 용어로 객체를 생성하거나 .build() 메소드를 호출하는 것이 .build() 입니다.


당신의 패턴으로 어떤 문제를 해결하려고합니까? 빌더 패턴은 다른 생성자 또는 매우 긴 생성자를 막기 위해 많은 (선택적) 매개 변수가있는 오브젝트에 사용됩니다. 또한 생성하는 동안 객체를 일관된 상태 (대 javabean 패턴)로 유지합니다.

빌더와 "설정 객체"(좋은 이름 인 것 같음)의 차이점은 생성자 또는 getter / setter가 동일한 매개 변수를 사용하여 객체를 만들어야한다는 것입니다. 이것은 a) 생성자 문제를 해결하지 못하거나 b) config 객체를 일관성없는 상태로 유지합니다. 구성 개체의 일관성없는 상태로 인해 실제로 손상되지는 않지만 완료되지 않은 구성 개체를 매개 변수로 전달할 수 있습니다. [팬텀 유형에 대한 미시드 링크는이 문제를 해결하는 것으로 보이지만 다시 읽기 쉽도록합니다 ( new Foo<TRUE,TRUE, TRUE, FALSE, TRUE> 약간의 실수).] 그게 빌더 패턴의 큰 장점 은요. 객체를 생성하기 전에 모든 하위 유형 (공장처럼 작동)을 반환 할 수 있습니다.

구성 개체는 모든 필수 매개 변수 집합에 대해 유효합니다. .NET이나 java에서는이 패턴을 여러 번 보았습니다.


이미 지적한 바와 같이 빌더 패턴의 몇 가지 장점을 잃어 가고 있습니다 ( 빌더는 깨끗한 빌더에 비해 세부 사항을 유지하고 누출하기가 어렵습니다).

그러나 내가 가장 그리워하는 것은 빌더 패턴을 사용하여 "유창한 인터페이스" 를 제공하는 것입니다.

대신 이것 :

ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);

넌 할 수있어:

ProductFactory.create()
    .drink("Vodka")
    .whereAlcohoolLevelIs(0.38)
    .inABottleSized(0.7)
    .pricedAt(17.99)
    .build();

모든 사람들이 유창한 인터페이스를 좋아하지는 않지만 빌더 패턴을 아주 잘 사용하고 있습니다 (모든 유창한 인터페이스는 빌더 패턴을 사용해야하지만 모든 빌더 패턴이 유창한 인터페이스는 아님).

Google 콜렉션과 같은 훌륭한 Java 콜렉션은 "유창한 인터페이스"를 매우 자유롭고 아주 잘 사용합니다. 나는 당신의 "더 쉬운 타입 / 적은 캐릭터들" 접근법에 대해 언젠가 이것을 고를 것이다.)


필자는 개인적으로 첫눈에 알맞은 빌더 패턴이 실제로이 오브젝트가 사용되는 코드를보다 깨끗하게 제공한다고 생각합니다. 반면에 getter / setter를 사용하지 않으면 낙타의 경우 getter / setter를 기대하는 많은 프레임 워크에서 사용할 수 없습니다. 이것은 내가 느끼는 심각한 무승부입니다.

나가 getters / setters로 또한 좋아하는 무엇을 너가하고있는 것을 명확하게 본다이다 : 얻거나 놓으 십시요. 나는 빌더와 함께 여기서 약간의 직관적 인 명확성을 잃어 가고 있다고 느낍니다.

많은 사람들이 특정 책을 읽었으며 갑자기 그 빌더 패턴이 새로운 iPhone처럼 과장된 것을 즐겼습니다. 그러나 나는 얼리 어답터가 아닙니다. 성능, 유지 보수, 코딩 등 모든 영역에서 정말 큰 시간을 절약 할 수있을 때만 "새로운 방식"을 사용합니다 ...

내 실무 경험은 일반적으로 getters / setter 및 생성자를 사용하는 것이 좋습니다. 그것은 내가 어떤 목적을 위해이 POJO를 재사용하도록 허용한다.

Config 오브젝트의 목적을 알았지 만 빌더보다 오버 헤드가 더 많다고 생각합니다. 세터는 무엇이 잘못 되었습니까?

어쩌면 우리는 WITH 절을 만들어야 할 것입니다 : 예를 들어,

public Class FooBar() {
    private String foo;

    public void setFoo(String bar) { 
      this.foo = bar; 
    }

    public String getFoo() { 
        return this.foo; 
    }
}

public static void main(String []args) {

 FooBar fuBar = new FooBar();
 String myBar;

 with fuBar {
    setFoo("bar");
    myBar = getFoo(); 
 }
}

아, 몰라요 ... 나는 이것이 내부 클래스의 번거 로움없이 코드 작성을 빨리 할 수 ​​있다고 생각합니다. 누구든지 Oracle Java 전문가와 관련이 있습니까?

빌더와 함께 객체를 사용하는 것처럼 깨끗하지는 않지만 빌더 생성 시간을 절약 할 수 있습니다. 그리고 클래스를 프레임 워크에서 사용할 수있는 정규 pojo / bean으로 사용할 수 있습니다 ...

너희들이 실제로이 조항을 좋아하든 그렇지 않다고 생각하니? 건배





builder-pattern