Шаблон Builder Джошуа Блоха и предупреждения PMD

Я написал класс, используя шаблон Builder Джошуа Блоха, который похож на этот пример Pizza:

public class Pizza {
  private int size;
  private boolean cheese;
  private boolean pepperoni;
  private boolean bacon;

  public static class Builder {
    //required
    private final int size;

    //optional
    private boolean cheese = false;
    private boolean pepperoni = false;
    private boolean bacon = false;

    public Builder(int size) {
      this.size = size;
    }

    public Builder cheese(boolean value) {
      cheese = value;
      return this;
    }

    public Builder pepperoni(boolean value) {
      pepperoni = value;
      return this;
    }

    public Builder bacon(boolean value) {
      bacon = value;
      return this;
    }

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

  private Pizza(Builder builder) {
    size = builder.size;
    cheese = builder.cheese;
    pepperoni = builder.pepperoni;
    bacon = builder.bacon;
  }
}

но PMD сообщил о 2 предупреждениях:

  1. (указывая на метод Builder.build()) Избегайте создания экземпляров через частные конструкторы вне класса конструктора. Создание экземпляров с помощью частных конструкторов вне класса конструктора часто вызывает создание метода доступа. Избавиться от этой ситуации может фабричный метод или неприватизация конструктора. Сгенерированный файл класса на самом деле является интерфейсом. Это дает доступному классу возможность вызывать новый конструктор скрытой области пакета, который принимает интерфейс в качестве дополнительного параметра. Это эффективно превращает частный конструктор в конструктор с областью действия пакета, и его сложно различить.
  2. Класс не может быть создан и не предоставляет никаких статических методов или полей. Нельзя использовать класс с закрытыми конструкторами и без каких-либо статических методов или полей.

Должен ли я просто игнорировать эти предупреждения?

Еще вопрос: приватные поля в классах Pizza и Builder дублируются. Это будет раздражать, когда количество приватных полей станет больше. Есть ли способ избежать этого?


person chance    schedule 30.05.2011    source источник
comment
для 1. вы могли бы определить свой конструктор Pizza как защищенный пакетом. Это также должно решить 2.   -  person Rom1    schedule 30.05.2011
comment
Образец хороший; PMD просто тупой. Я бы лично проигнорировал.   -  person skaffman    schedule 30.05.2011
comment
просто примечание: я использовал крошечный фреймворк под названием Make-It-Easy. некоторое время, что помогает строить эти конструкторы, а код вполне читабелен. Я использую его в основном для своих модульных тестов, но его определенно можно использовать для производственного кода.   -  person Augusto    schedule 30.05.2011
comment
Я бы проигнорировал первое предупреждение PMD. А вот второе оправдано. Ваш класс Pizza, как и в вашем вопросе, непригоден для использования, поскольку у него нет никакого метода. Добавьте несколько геттеров, и PMD больше не будет жаловаться (надеюсь).   -  person JB Nizet    schedule 30.05.2011


Ответы (2)


О том, как убрать дублирование.

Я получу больше отрицательных голосов :) А может быть, что-то вроде этого?

class Pizza {
private int size;
private boolean cheese;
private boolean pepperoni;
private boolean bacon;

public static class Builder {
    private Pizza pizza = new Pizza();

    public Builder(int size) {
        pizza.size = size;
    }

    public Builder cheese(boolean value) {
        pizza.cheese = value;
        return this;
    }

    public Builder pepperoni(boolean value) {
        pizza.pepperoni = value;
        return this;
    }

    public Builder bacon(boolean value) {
        pizza.bacon = value;
        return this;
    }

    public Pizza build() {
        return pizza;
    }
}

private Pizza() {
}
}
person weekens    schedule 30.05.2011
comment
Мне это не нравится. Вы можете изменить поля в своей пицце через Builder, который создал ее ПОСЛЕ вызова build(). Одно из лучших применений шаблона построителя — упростить создание неизменяемых классов. - person Kenny; 08.08.2012
comment
Извините, но с этим стилем вы также получаете: 1) Избегайте создания экземпляров через частные конструкторы вне класса конструктора. и 2) класс не может быть создан и не предоставляет никаких статических методов или полей, поэтому проблемы не решаются. - person aloplop85; 26.08.2013

частные поля в классах Pizza и Builder дублируются. Это будет раздражать, когда количество приватных полей станет больше. Есть ли способ избежать этого?

Я лично обхожу это, используя третий закрытый класс объекта статического значения, который содержит все поля и использует его как в построителе, так и в основном классе (доступ к полям обрабатывается делегированием). Конечно, это может привести к увеличению количества строк/классов, но это бесценно, если ваши конструкторы окажутся сложными с большим количеством полей и проверок.

Кроме того, не помешает предоставить статический метод в классе Pizza, который создает объект Pizza с обязательными полями. Если, конечно, вы не уверены, что такое обязательные поля, или боитесь, что обязательные поля могут измениться в ходе эволюции вашего класса. Дело в том, что пока вы можете оправдать свои действия после долгих размышлений (как выразился Джошуа Блох), вы можете спокойно игнорировать эти предупреждения, зная, что вы знаете, что делаете. :-)

Отрывок на выброс:

public class Pizza {

    private final PizzaVO vo;

    private static class PizzaVO {

        int size;

        boolean cheese;

        boolean pepperoni;

        boolean bacon;
    }

    public static class Builder {

        private final PizzaVO vo = new PizzaVO();

        public Builder(int size) {
            vo.size = size;
        }

        public Builder cheese(boolean value) {
            vo.cheese = value;
            return this;
        }

        public Builder pepperoni(boolean value) {
            vo.pepperoni = value;
            return this;
        }

        public Builder bacon(boolean value) {
            vo.bacon = value;
            return this;
        }

        public Pizza build() {
            return new Pizza(vo);
        }
    }

    private Pizza(PizzaVO vo) {
        this.vo = vo;
    }

    public int getSize() {
        return vo.size;
    }

    // other getter setter methods as per your taste

}
person Sanjay T. Sharma    schedule 30.05.2011
comment
Не стоит использовать этот стиль. Проблемы не решаются. - person aloplop85; 26.08.2013
comment
привет aloplop85, можешь уточнить какие проблемы не решаются и почему? - person sudocoder; 28.01.2015
comment
@sudocoder как минимум ошибка PMD Избегайте создания экземпляров через частные конструкторы вне класса конструктора. еще бы присутствовал - person Shannon; 29.06.2017