Улучшения в шаблоне дизайна Джошуа Блоха? - PullRequest
3 голосов
/ 17 июня 2011

В 2007 году я прочитал статью о том, как Джошуа Блохс рассматривает «шаблон строителя» и как его можно изменить, чтобы улучшить чрезмерное использование конструкторов и сеттеров, особенно когда объект имеет большое количество свойств, большинство из которыхявляются необязательными.Краткое описание этого шаблона проектирования изложено здесь [http://rwhansen.blogspot.com/2007/07/theres-builder-pattern-that-joshua.html].

. Мне понравилась идея, и я использую ее с тех пор.Проблема с этим, хотя он очень чистый и приятный в использовании с точки зрения клиента, его реализация может быть проблемой для задницы!В объекте так много разных мест, где одно свойство является ссылкой, и, таким образом, создание объекта и добавление нового свойства занимает много времени.

Итак ... У меня была идея.Во-первых, пример объекта в стиле Джошуа Блоха:

Джош Блох Стиль:

public class OptionsJoshBlochStyle {

    private final String option1;
    private final int option2;
    // ...other options here  <<<<

    public String getOption1() {
        return option1;
    }

    public int getOption2() {
        return option2;
    }

    public static class Builder {

        private String option1;
        private int option2;
        // other options here <<<<<

        public Builder() {
        }

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

        public Builder option2(int option2) {
            this.option2 = option2;
            return this;
        }

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

    private OptionsJoshBlochStyle(Builder builder) {
        this.option1 = builder.option1;
        this.option2 = builder.option2;
        // other options here <<<<<<
    }

    public static void main(String[] args) {
        OptionsJoshBlochStyle optionsVariation1 = new OptionsJoshBlochStyle.Builder().option1("firefox").option2(1).build();
        OptionsJoshBlochStyle optionsVariation2 = new OptionsJoshBlochStyle.Builder().option1("chrome").option2(2).build();
    }
}

Теперь моя «улучшенная» версия:

public class Options {

    private String option1;
    private int option2;
    // ...other options here

    public String getOption1() {
        return option1;
    }

    public int getOption2() {
        return option2;
    }

    public static class Builder {

        private final Options options;

        public Builder() {
            this.options = new Options();
        }

        public Builder option1(String option1) {
            this.options.option1 = option1;
            return this;
        }

        public Builder option2(int option2) {
            this.options.option2 = option2;
            return this;
        }

        public Options build() {
            return options;//new RequestContext(this);
        }
    }

    private Options() {

    }

    public static void main(String[] args) {
        Options optionsVariation1 = new Options.Builder().option1("firefox").option2(1).build();
        Options optionsVariation2 = new Options.Builder().option1("chrome").option2(2).build();

    }
}

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

Есть ли какой-либо недостаток в этом улучшении ремонтопригодности?Должна быть причина, по которой он повторил свойства внутри вложенного класса, которые я не вижу?

PS: Не уверен, подходит ли этот тип вопроса для StackOverflow или относится к чему-то более субъективному, как [programmers.stackexchange.com], поэтому я заранее извиняюсь!

РЕДАКТИРОВАТЬ 1:

@ irreputable - Что-то подобное в Java?Я до сих пор не понимаю, как это становится потокобезопасным из-за изменений.Мне нужно изучить безопасную публикацию, как вы предлагаете.

public class OptionsDelegate {

    private final OptionsData data;

    private static class OptionsData {
         String option1;
         int option2;
    }

    // ...other options here

    public String getOption1() {
        return data.option1;
    }

    public int getOption2() {
        return data.option2;
    }

    public static class Builder {

        private OptionsData data;

        public Builder() {
            this.data = new OptionsData();
        }

        public Builder option1(String option1) {
            this.data.option1 = option1;
            return this;
        }

        public Builder option2(int option2) {
            this.data.option2 = option2;
            return this;
        }

        public OptionsDelegate build() {
            OptionsDelegate optionsDelegate = new OptionsDelegate(this.data);
            this.data = null;
            return optionsDelegate;
        }
    }

    private OptionsDelegate(OptionsData data) {
        this.data = data;
    }

    public static void main(String[] args) {
        OptionsDelegate optionsVariation1 = new OptionsDelegate.Builder().option1("firefox").option2(1).build();
        OptionsDelegate optionsVariation2 = new OptionsDelegate.Builder().option1("chrome").option2(2).build();


    }
}

Ответы [ 4 ]

11 голосов
/ 17 июня 2011

(Я ожидаю, что этот вопрос будет перенесен, но я все равно отвечу - ответ будет сохранен.)

Вы утверждаете, что класс все еще неизменен ... но я не думаю, что это так.

Options.Builder builder = new Options.Builder().option1("foo").option2(1);
Options options = builder.build();

builder.option1("changed");
System.out.println(options.getOption1());

Обратите внимание, что с небольшим изменением для предотвращения этого (установите options в null в методе build(), чтобы конструктор не мог быть повторно использован), это в основном шаблон, который реализует Java Протокол Буферы используется для использования. Я верю, что сейчас использует нечто более похожее на более раннюю модель Джоша.

4 голосов
/ 17 июня 2011

final гарантирует «безопасную публикацию». Ваша версия не является поточно-ориентированной. Суть этой вещи заключается в том, как создать объекты с полями final более дружественным путем, удалив final, чтобы победить эту цель.

Это возможно, хотя:

class Options

    class Data
        option1;
        option2;        

    final Data data;
    Options(Data data){ this.data=data; }

    getOption1(){ return data.option1; }
    getOption2(){ return data.option2; }

    class Builder

        Data data = new Data();

        setOption1(op1){ data.option1=op1; }
        setOption2(op2){ data.option2=op2; }

        Options build()
        { 
            Options o = new Options(data); 
            data = null;
            return o;
        }
0 голосов
/ 10 мая 2013

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

0 голосов
/ 26 февраля 2013

Вы не можете использовать свою версию (первую) в случае, когда нельзя создать экземпляр класса Options в не полностью инициализированном состоянии, например, когда конструктор Options содержит проверку полей (option1, option2).Так что оригинальная версия Builder более гибкая.И как уже упоминалось выше, ваша вторая версия Builder создает изменяемый объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...