Шаблон Builder в эффективной Java - PullRequest
135 голосов
/ 15 февраля 2011

Я недавно начал читать «Эффективную 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();
    }
}

Я получаю следующую ошибку компилятора:

включающий экземпляр, содержащий effectivejava.BuilderPattern.NutritionalFacts.Builder необходимо NutritionalFacts n = new NutritionalFacts.Builder (10) .carbo (23) .fat (1) .build (); * +1012 *

Я не понимаю, что означает сообщение. Пожалуйста, объясни. Приведенный выше код аналогичен примеру, предложенному Блохом в его книге.

Ответы [ 9 ]

163 голосов
/ 15 февраля 2011

Сделать строителя классом static. Тогда это будет работать. Если он нестатический, ему потребуется экземпляр своего класса-владельца - и дело не в том, чтобы иметь его экземпляр, а даже в том, чтобы запретить создание экземпляров без компоновщика.

public class NutritionFacts {
    public static class Builder {
    }
}

Ссылка: Вложенные классы

27 голосов
/ 31 июля 2015

Вы должны сделать класс Builder статическим, а также сделать поля окончательными и иметь методы получения, чтобы получить эти значения.Не предоставляйте установщики для этих значений.Таким образом, ваш класс будет совершенно неизменным.

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();
13 голосов
/ 31 января 2014

Чтобы создать внутренний компоновщик в Intellij IDEA, проверьте этот плагин: https://github.com/analytically/innerbuilder

11 голосов
/ 15 февраля 2011

Вы пытаетесь получить доступ к нестатическому классу статическим способом. Измените Builder на static class Builder, и оно должно работать.

Пример использования, который вы приводите, не работает, потому что нет экземпляра Builder. Статический класс для всех практических целей всегда создается. Если вы не сделаете это статичным, вам нужно будет сказать:

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

Потому что вам нужно будет каждый раз создавать новый Builder.

7 голосов
/ 15 февраля 2011

Вам необходимо объявить внутренний класс Builder как static.

Обратитесь к некоторой документации по нестатическим внутренним классам и статическим внутренним классам .

По существу, экземпляры нестатических внутренних классов не могут существовать безприкрепленный экземпляр внешнего класса.

4 голосов
/ 10 ноября 2018

Когда у вас есть идея, на практике вы можете найти @Builder в lombok гораздо более удобным.

@Builder позволяет автоматически создавать код, необходимый для создания экземпляра вашего класса с таким кодомкак:

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

Официальная документация: https://www.projectlombok.org/features/Builder

4 голосов
/ 15 февраля 2011

Класс Builder должен быть статическим. У меня сейчас нет времени, чтобы на самом деле протестировать этот код, но если он не работает, дайте мне знать, и я еще раз посмотрю.

4 голосов
/ 15 февраля 2011

Это означает, что вы не можете создать вложенный тип.Это означает, что сначала вы должны создать экземпляр «родительского» класса, а затем из этого экземпляра вы можете создавать вложенные экземпляры классов.

NutritionalFacts n = new NutritionalFacts()

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

Вложенные классы

1 голос
/ 17 мая 2018

Я лично предпочитаю использовать другой подход, когда у вас есть 2 разных класса. Так что вам не нужен статический класс. Это в основном, чтобы избежать записи Class.Builder, когда вам нужно создать новый экземпляр.

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

Итак, вы можете использовать своего строителя так:

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