it-swarm.com.de

Builder-Muster in Effective Java

Ich habe kürzlich begonnen, Effective Java von Joshua Bloch. Ich fand die Idee des Builder-Musters [Punkt 2 im Buch] wirklich interessant. Ich habe versucht, es in meinem Projekt zu implementieren, aber es gab Kompilierungsfehler: Das Folgende ist im Wesentlichen das, was ich versucht habe:

Die Klasse mit mehreren Attributen und ihre Builder-Klasse:

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

Klasse, in der ich versuche, die oben genannte Klasse zu verwenden:

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

Ich erhalte den folgenden Compilerfehler:

eine einschließende Instanz, die effectivejava.BuilderPattern.NutritionalFacts.Builder enthält, ist erforderlich NutritionalFacts n = new NutritionalFacts.Builder (10) .carbo (23) .fat (1) .build ();

Ich verstehe nicht, was die Nachricht bedeutet. Bitte erkläre. Der obige Code ähnelt dem von Bloch in seinem Buch vorgeschlagenen Beispiel.

136
Swaranga Sarma

Machen Sie den Builder zu einer static -Klasse. Dann wird es funktionieren. Wenn es nicht statisch ist, ist eine Instanz der Klasse, die es besitzt, erforderlich - und der Punkt ist, keine Instanz davon zu haben und sogar das Erstellen von Instanzen ohne den Builder zu verbieten.

public class NutritionFacts {
    public static class Builder {
    }
}

Referenz: verschachtelte Klassen

166
Bozho

Sie sollten die Builder-Klasse als statisch festlegen und außerdem die Felder als endgültig festlegen und über Getter verfügen, um diese Werte abzurufen. Geben Sie keine Setter für diese Werte an. Auf diese Weise wird Ihre Klasse vollkommen unveränderlich.

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

Und jetzt können Sie die Eigenschaften wie folgt einstellen:

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

Um einen inneren Builder in Intellij IDEA zu generieren, überprüfen Sie dieses Plugin: https://github.com/analytically/innerbuilder

13
analytically

Sie versuchen, statisch auf eine nicht statische Klasse zuzugreifen. Ändern Sie Builder in static class Builder und es sollte funktionieren.

Die von Ihnen angegebene Beispielverwendung schlägt fehl, da keine Instanz von Builder vorhanden ist. Eine statische Klasse für alle praktischen Zwecke wird immer instanziiert. Wenn Sie es nicht statisch machen, müssen Sie sagen:

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

Weil Sie jedes Mal ein neues Builder erstellen müssten.

11
Michael K

Sie müssen die innere Klasse Builder als static deklarieren.

Lesen Sie die Dokumentation für nicht statische innere Klassen und statische innere Klassen .

Grundsätzlich können die nicht statischen Instanzen der inneren Klassen nicht ohne angehängte Instanz der äußeren Klasse existieren.

7

Wenn Sie eine Idee haben, können Sie in der Praxis lomboks @Builder viel bequemer.

@Builder lässt Sie automatisch den Code erzeugen, der erforderlich ist, damit Ihre Klasse mit folgendem Code instanziierbar ist:

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

Offizielle Dokumentation: https://www.projectlombok.org/features/Builder

5
torina

Dies bedeutet, dass Sie keinen einschließenden Typ erstellen können. Dies bedeutet, dass Sie zuerst eine Instanz der übergeordneten Klasse erstellen müssen und dann aus dieser Instanz geschachtelte Klasseninstanzen erstellen können.

NutritionalFacts n = new NutritionalFacts()

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

verschachtelte Klassen

Die Builder-Klasse sollte statisch sein. Ich habe momentan keine Zeit, den Code darüber hinaus zu testen, aber wenn es nicht funktioniert, lass es mich wissen und ich werde es mir noch einmal ansehen.

4
Shaun

Ich persönlich bevorzuge den anderen Ansatz, wenn Sie 2 verschiedene Klassen haben. Sie brauchen also keine statische Klasse. Dies ist grundsätzlich zu vermeiden, schreiben Class.Builder wenn Sie eine neue Instanz erstellen müssen.

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);
    }
    // ....
}

Sie können Ihren Builder also folgendermaßen verwenden:

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

Sie müssen Builder class in statische Klasse Builder ändern. Dann wird es gut funktionieren.

0
krishna kirti

Wie bereits erwähnt, müssen Sie die Klasse static erstellen. Nur eine kleine Ergänzung - wenn Sie möchten, gibt es einen etwas anderen Weg ohne statischen.

Bedenken Sie. Implementieren eines Builders durch Deklarieren von Setzern vom Typ withProperty(value) in der Klasse und Zurückgeben eines Verweises auf sich selbst. Bei diesem Ansatz haben Sie eine einzelne und eine elegante Klasse, die threadsicher und prägnant ist.

Bedenken Sie:

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");

Weitere Beispiele finden Sie unter Java Builder .

0
Johnny