Подтвердить что ты не робот

Каков пересмотренный шаблон строителя?

В чем разница между исходным шаблоном Builder от GoF и "измененным шаблоном GoF Builder" Джошуа Блоха?

4b9b3361

Ответ 1

Образец GoF фокусируется на абстрагировании шагов построения, так что, изменяя построитель, вы можете получать разные результаты, в то время как "пересмотренный строитель" нацеливается на проблему ненужной сложности, добавляемую несколькими конструкторами. Таким образом, шаблон GoF больше связан с абстракцией, а пересмотренный patter - более простота (IMO).

Посмотрите примеры в http://en.wikipedia.org/wiki/Builder_pattern и http://rwhansen.blogspot.com/2007/07/theres-builder-pattern-that-joshua.html, и это должно быть совершенно ясно.

Ответ 2

Примечание re Ответ Микко: Пример Хансена имеет несколько проблем - или, по крайней мере, отличия от версии Блоха, хотя я считаю, что версия Блоха превосходит.

В частности:

Сначала поля Widget устанавливаются в Builder.build(), а не в конструкторе Widget, и поэтому не являются (и не могут быть) final. Widget считается неизменным, но нет ничего, что могло бы помешать другому программисту продвигаться вперед и позже добавлять сеттеры.

Во-вторых, комментарии в методе построения Hansen говорят, что "предварительная проверка подлинности идет здесь". Блох (EJ 2ed. С .15) говорит:

Очень важно проверить [инварианты] после копирования параметров от строителя к объекту и проверить их на поля объектов, а не на поля построителя (пункт 39).

Если вы перешли к пункту 39 (стр. 185), вы увидите рассуждения:

[Это] защищает класс от изменений параметров из другого потока во время "окна уязвимости" между моментом проверки параметров и временем их копирования.

Поля в Widget являются неизменяемыми и не требуют какого-либо оборонительного копирования, но тем не менее безопаснее просто придерживаться правильного шаблона, если кто-то приходит и добавляет Date или массив или некоторые изменчивые Collection позже. (Он также защищает от другого потока, изменяя Builder в середине вызова build(), но это довольно узкое окно безопасности, поэтому, вероятно, лучше всего сделать так, чтобы Builder не делились между потоками.)

Более блоховская версия будет:

public class Widget {
    public static class Builder {
        private String name;
        private String model;
        private String serialNumber;
        private double price;
        private String manufacturer;

        public Builder( String name, double price ) {
            this.name = name;
            this.price = price;
        }

        public Widget build() {
            Widget result = new Widget(this);

            // *Post*-creation validation here

            return result;
        }

        public Builder manufacturer( String value ) {
            this.manufacturer = value;
            return this;
        }

        public Builder serialNumber( String value ) {
            this.serialNumber = value;
            return this;
        }

        public Builder model( String value ) {
            this.model = value;
            return this;
        }
    }

    private final String name;
    private final String model;
    private final String serialNumber;
    private final double price;
    private final String manufacturer;

    /**
     * Creates an immutable widget instance.
     */
    private Widget( Builder b ) {
        this.name = b.name;
        this.price = b.price;
        this.model = b.model;
        this.serialNumber = b.serialNumber;
        this.manufacturer = b.manufacturer;
    }

    // ... etc. ...
}

Все поля Widget теперь final, и все они проверяются после построения.