it-swarm-ru.tech

Шаблон Builder в действующем Java

Недавно я начал читать книгу "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();
    }
}

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

включающий экземпляр, который содержитffectivejava.BuilderPattern.NutritionalFacts.Builder требуется NutritionalFacts n = new NutritionalFacts.Builder (10) .carbo (23) .fat (1) .build ();

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

134
Swaranga Sarma

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

public class NutritionFacts {
    public static class Builder {
    }
}

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

160
Bozho

Вы должны сделать класс 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();
27
Raj Hassani

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

13
analytically

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

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

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

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

11
Michael K

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

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

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

7
Grzegorz Oledzki

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

NutritionalFacts n = new NutritionalFacts()

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

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

4
Damian Leszczyński - Vash

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

4
Shaun

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

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

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

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

3
torina

Я лично предпочитаю использовать другой подход, когда у вас есть 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();
1
winter