Etkili Java'da Oluşturucu Kalıbı


137

Geçenlerde Joshua Bloch'un Etkili Java'sını okumaya başladım. Builder kalıbı fikrini [Kitaptaki 2. madde] gerçekten ilginç buldum. Projemde uygulamaya çalıştım ama derleme hataları vardı. Aslında yapmaya çalıştığım şey şu:

Birden çok niteliğe sahip sınıf ve oluşturucu sınıfı:

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;
    }
}

Sınıf nerede yukarıdaki sınıf kullanmaya çalışın:

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

Aşağıdaki derleyici hatası alıyorum:

activejava.BuilderPattern.NutritionalFacts.Builder içeren bir çevreleme örneği NutritionalFacts n = new NutritionalFacts.Builder (10) .carbo (23) .fat (1) .build ();

Mesajın ne anlama geldiğini anlamıyorum. Lütfen açıkla. Yukarıdaki kod Bloch tarafından kitabında önerilen örneğe benzer.


Yanıtlar:


171

Oluşturucuyu bir staticsınıf yapın. Sonra işe yarayacak. Statik değilse, kendi sınıfının bir örneğini gerektirir - ve nokta bunun bir örneğine sahip olmak ve hatta oluşturucu olmadan örnek oluşturmayı yasaklamaktır.

public class NutritionFacts {
    public static class Builder {
    }
}

Referans: İç içe sınıflar


34
Ve aslında, Builderbir statickitapta (sayfa 14, 2. Baskı, hat 10) örnekte.
Powerlord

27

Builder sınıfını statik olarak yapmalısınız ve ayrıca alanları sonlandırmalı ve bu değerleri almak için alıcılara sahip olmalısınız. Bu değerlere ayarlayıcı sağlamayın. Bu şekilde sınıfınız kusursuz bir şekilde değişmez olacaktır.

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;
    }
}

Ve şimdi özellikleri aşağıdaki gibi ayarlayabilirsiniz:

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

Neden sadece NutritionalFacts alanlarını kamuya açıklamıyorsunuz? Zaten nihailar ve hala değişmez olacaktı.
skia.heliou

finalalanlar yalnızca başlatma sırasında alanlara her zaman ihtiyaç duyulursa anlamlıdır. Değilse, alanlar olmamalıdır final.
Piotrek Hryciuk

12

Statik olmayan bir sınıfa statik bir şekilde erişmeye çalışıyorsunuz. Değişim Builderiçin static class Builderve onunla çalışmalı.

Hediye örneği olmadığından verdiğiniz örnek kullanım başarısız oluyor Builder. Tüm pratik amaçlar için statik bir sınıf her zaman somutlaştırılır. Statik yapmazsanız şunu söylemeniz gerekir:

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

Çünkü Builderher seferinde yeni bir tane inşa etmeniz gerekir .




5

Bir fikriniz olduğunda, pratikte, lombok'un @Builderçok daha uygun olduğunu görebilirsiniz.

@Builder sınıfınızın aşağıdaki gibi örneklerle somutlaştırılması için gerekli kodu otomatik olarak oluşturmanıza olanak tanır:

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

Resmi belgeler: https://www.projectlombok.org/features/Builder


4

Bu, zarf tipi oluşturamayacağınız anlamına gelir. Bu, önce "üst" sınıfın bir örneğini oluşturmanız ve daha sonra bu örnekten iç içe sınıf örnekleri oluşturabileceğiniz anlamına gelir.

NutritionalFacts n = new NutritionalFacts()

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

İç İçe Sınıflar


3
bu pek mantıklı değil, çünkü "gerçekleri" oluşturmak için kurucuya ihtiyacı var, tersi değil.
Bozho

5
doğru oluşturucu desenine odaklanırsak, yalnızca "mesajın ne anlama geldiğini anlamadım" üzerine odaklandım ve iki çözümden birini sundum.
Damian Leszczyński - Vash

3

Builder sınıfı statik olmalıdır. Aslında bunun ötesinde kodu test etmek için zamanım yok, ama işe yaramazsa bana bildirin ve başka bir göz atacağım.


1

Kişisel olarak 2 farklı sınıfınız olduğunda diğer yaklaşımı kullanmayı tercih ediyorum. Yani herhangi bir statik sınıfa ihtiyacınız yok. Bu, temel olarak Class.Builderyeni bir örnek oluşturmanız gerektiğinde yazmayı önlemek içindir .

public class Person {
    private String attr1;
    private String attr2;
    private String attr3;

    // package access
    Person(PersonBuilder builder) {
        this.attr1 = builder.getAttr1();
        // ...
    }

    // ...
    // getters and setters 
}

public class PersonBuilder (
    private String attr1;
    private String attr2;
    private String attr3;

    // constructor with required attribute
    public PersonBuilder(String attr1) {
        this.attr1 = attr1;
    }

    public PersonBuilder setAttr2(String attr2) {
        this.attr2 = attr2;
        return this;
    }

    public PersonBuilder setAttr3(String attr3) {
        this.attr3 = attr3;
        return this;
    }

    public Person build() {
        return new Person(this);
    }
    // ....
}

Böylece, kurucunuzu şu şekilde kullanabilirsiniz:

Person person = new PersonBuilder("attr1")
                            .setAttr2("attr2")
                            .build();

0

Burada daha önce belirtildiği gibi sınıfı yapmanız gerekir static. Sadece küçük bir ekleme - isterseniz, statik olan olmadan biraz farklı bir yol var.

Bunu düşün. withProperty(value)Sınıf içindeki tür ayarlayıcılar gibi bir şey bildirerek bir yapıcı uygulamak ve kendilerine bir referans döndürmelerini sağlamak. Bu yaklaşımda, güvenli ve özlü bir iplik olan tek ve zarif bir sınıfınız var.

Bunu düşün:

public class DataObject {

    private String first;
    private String second;
    private String third;

    public String getFirst(){
       return first; 
    }

    public void setFirst(String first){
       this.first = first; 
    }

    ... 

    public DataObject withFirst(String first){
       this.first = first;
       return this; 
    }

    public DataObject withSecond(String second){
       this.second = second;
       return this; 
    }

    public DataObject withThird(String third){
       this.third = third;
       return this; 
    }
}


DataObject dataObject = new DataObject()
     .withFirst("first data")
     .withSecond("second data")
     .withThird("third data");

Daha fazla Java Builder örneği için göz atın .


0

Builder sınıfını statik sınıf Builder olarak değiştirmeniz gerekir . Sonra iyi çalışır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.