Вдосконалення структури дизайнера Джошуа Блоха?


12

Ще в 2007 році я прочитав статтю про те, що Джошуа Блохс сприймає "модель побудови" та про те, як це можна змінити для покращення надмірного використання конструкторів та сеттерів, особливо коли об'єкт має велику кількість властивостей, більшість з яких є необов'язковими. Короткий виклад цього шаблону є articled тут .

Ідея мені сподобалась, і я її використовую з тих пір. Проблема з цим, хоча він дуже чистий і приємний у використанні з точки зору клієнта, реалізовуючи це, може бути біль у груднині! В об’єкті так багато різних місць, де одна властивість є посиланням, і, таким чином, створюється об'єкт, і додавання нового властивості займає багато часу.

Отже ... у мене була ідея. По-перше, приклад об’єкта в стилі Джошуа Блоха:

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

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 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 {

    // note that these are not final
    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 = 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;
        }
    }

    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 місця, в яких нам потрібно додати код про будь-які властивості додавання (або варіанти, в цьому випадку)! Єдиний мінус, який я бачу, - це те, що змінні екземпляри зовнішнього класу не можуть бути остаточними. Але, клас все ще незмінний без цього.

Чи дійсно є якийсь мінус у цьому покращенні ремонту? Має бути причина, за якою він повторював властивості вкладеного класу, які я не бачу?


Це здається дуже схожим на моє взяття малюнка будівельника в C # тут .
MattDavey

Відповіді:


12

Ваша варіація досить приємна. Але це дозволяє користувачам робити це:

Options.Builder builder = new Options.Builder().option1("firefox").option2(1);
Options optionsVariation1 = builder.build();
assert optionsVariation1.getOption1().equals("firefox");
builder.option1("chrome");
assert optionsVariation1.getOption1().equals("firefox"); // FAILURE!

Що швидше перемагає об’єкт.

Ви можете змінити buildметод для цього:

public Options build() {
    Options options = this.options;
    this.options = null;
    return options;
}

Що перешкоджало б цьому - будь-який виклик методу встановлення програми для будівельника після buildвиклику завершиться невдачею з NullPointerException. Якщо ви хочете отримати flash, ви можете навіть перевірити на null і замість цього викинути IllegalStateException або щось інше. І ви можете перемістити це до загального базового класу, де його можна було б використовувати для всіх будівельників.


1
Я б змінити на 2 - й лінії в build()до: this.options = new Options();. Таким чином, екземпляри "Опції" були б безпечно незмінними, плюс конструктор одночасно використовувався б.
Натікс

5

Конструктор у шаблоні Блоха може використовуватися багато разів для створення об'єктів, які "в основному" однакові. Крім того, незмінні об'єкти (усі поля остаточні і самі непорушні) мають переваги безпеки потоку, які можуть змінити ваші зміни.


0

Якщо Параметри були ефективно клоновані (я маю на увазі, незалежно від інтерфейсу Cloneable), ви можете використовувати шаблон прототипу - мати його у builder та клонувати його у build ().

Якщо ви не використовуєте інтерфейс Cloneable, вам доведеться скопіювати кожне поле, щоб воно додало ще одне місце, куди потрібно його додати, так що принаймні для класу з простими полями, фактично використовуючи Cloneable, було б хорошою ідеєю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.