Проверка шаблона построителя - Эффективная Java

В пункте 2 книги «Эффективная Java» (2-е издание) автор упоминает следующее о наложении инвариантов на параметры при использовании Builders:

Крайне важно, чтобы они проверялись после копирования параметров из построителя в объект и чтобы они проверялись в полях объекта, а не в полях построителя (пункт 39). Если какие-либо инварианты нарушены, метод сборки должен генерировать исключение IllegalStateException (статья 60).

Означает ли это, что после того, как метод сборки создал целевой объект, его следует передать процедуре проверки для любых необходимых проверок?

Кроме того, может ли кто-нибудь объяснить причину этого?


person Abhigyan Mehra    schedule 03.07.2016    source источник
comment
Я предполагаю, что поля построителя могут быть изменены (например, другим потоком) после того, как они были скопированы в объект, а это означает, что построитель может быть переведен в состояние, в котором он будет создавать действительный объект, тогда как ранее он создал недопустимый объект.   -  person Andy Turner    schedule 03.07.2016
comment
Извините, но я не уверен, правильно ли я это понимаю. Построитель будет создан как новый ‹ClassName›.Builder.setter1().setter2.().build(). Как любой другой поток может получить ссылку на этот конкретный объект Builder?   -  person Abhigyan Mehra    schedule 03.07.2016
comment
В таком случае нельзя. Но в общем случае нет ничего, что могло бы помешать совместному использованию экземпляра построителя между потоками: это просто ссылка, как и любая другая.   -  person Andy Turner    schedule 03.07.2016


Ответы (2)


Проверка объекта является неотъемлемой частью создания объекта с помощью компоновщиков. Хотя у вас может быть отдельная подпрограмма, выполняющая проверку, такое разделение не требуется: код проверки может быть частью функции, выполняющей сборку. Другими словами, вы можете сделать это

TargetObject build() {
    TargetObject res = new TargetObject();
    res.setProperty1();
    res.setProperty2();
    validate(res); // This call may throw an exception
    return res;
}

void validate(TargetObject obj) {
    if (...) {
        throw new IllegalStateException();
    }
}

или это:

TargetObject build() {
    TargetObject res = new TargetObject();
    res.setProperty1();
    res.setProperty2();
    if (...) {
        throw new IllegalStateException();
    }
    return res;
}

Важно то, что проверка происходит после, а не до построения целевого объекта. Другими словами, вам нужно проверять состояние объекта, а не состояние построителя.

person Sergey Kalinichenko    schedule 03.07.2016
comment
Другая важная вещь заключается в том, что вы проверяете объект, а не конструктор, потому что это объект, с которым вы собираетесь делать полезные вещи, поэтому он должен быть валидным. - person Andy Turner; 03.07.2016

Метод build() построителя обычно вызывает закрытый конструктор класса, который он создает. Вот почему строители обычно реализуются как статические вложенные классы, поэтому они имеют доступ к частному конструктору. Конструктор — это место, где происходит проверка. Даже если вы не используете шаблон построителя, конструкторы несут ответственность за то, чтобы объект находился в допустимом состоянии при его создании. И конструктор должен создавать защитные копии (элемент EJ 39) и проверять поля нового объекта, не поля построителя, потому что построитель может быть видоизменен во время копирования полей.

person Kevin Krumwiede    schedule 03.07.2016
comment
Это лучший ответ, чем принятый. +1 - person Manish Kumar Sharma; 22.06.2021