Это допустимая реализация Java неизменяемого класса и шаблона Builder? - PullRequest
9 голосов
/ 13 сентября 2010

Builder реализует Cloneable и переопределяет clone (), и вместо того, чтобы копировать каждое поле компоновщика, неизменный класс сохраняет приватный клон компоновщика.Это позволяет легко возвращать нового компоновщика и создавать слегка измененные копии неизменяемого экземпляра.

Таким образом, я могу пойти

MyImmutable i1 = new MyImmutable.Builder().foo(1).bar(2).build();
MyImmutable i2 = i1.builder().foo(3).build();

Интерфейс Cloneable, как говорят, несколько сломан, нонарушает ли это хорошую практику кодирования Java, есть ли проблемы с этой конструкцией?

final class MyImmutable { 
  public int foo() { return builder.foo; }
  public int bar() { return builder.bar; }
  public Builder builder() { return builder.clone(); }
  public static final class Builder implements Cloneable {
    public Builder foo(int val) { foo = val; return this; }
    public Builder bar(int val) { bar = val; return this; }
    public MyImmutable build() { return new MyImmutable(this.clone()); }
    private int foo = 0;
    private int bar = 0;
    @Override public Builder clone() { try { return (Builder)super.clone(); } catch(CloneNotSupportedException e) { throw new AssertionError(); } }
  }
  private MyImmutable(Builder builder) { this.builder = builder; }
  private final Builder builder;
}

Ответы [ 3 ]

6 голосов
/ 13 сентября 2010

Как правило, класс, построенный из Builder, не обладает какими-либо специализированными знаниями о Builder. То есть, в Immutable есть конструктор для предоставления значения для foo и bar:

public final class MyImmutable {
  public final int foo;
  public final int bar;
  public MyImmutable(int foo, int bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

Строителем будет отдельный класс:

public class MyImmutableBuilder {
  private int foo;
  private int bar;
  public MyImmutableBuilder foo(int val) { foo = val; return this; }
  public MyImmutableBuilder bar(int val) { bar = val; return this; }
  public MyImmutable build() { return new MyImmutable(foo, bar); }
}

Если вы хотите, вы можете добавить статический метод в конструктор MyImmutable, чтобы запустить его из существующего экземпляра MyImmutable:

public static MyImmutableBuilder basedOn(MyImmutable instance) {
  return new MyImmutableBuilder().foo(instance.foo).bar(instance.bar);
}
3 голосов
/ 14 сентября 2010

Ваша реализация похожа на реализацию, подробно описанную в издании Джоша Блоха «Эффективное Java» 2-е издание.

Одним из спорных вопросов может быть ваш build() метод.Если один сборщик создает неизменный экземпляр, было бы справедливо разрешить использование сборщика снова, учитывая, что его работа уже выполнена?Здесь предостережение заключается в том, что даже если вы создаете неизменный объект, изменчивость вашего компоновщика может привести к нескольким довольно «удивительным» ошибкам.

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

3 голосов
/ 13 сентября 2010

Я не видел такого подхода раньше, но похоже, что он будет работать нормально.

По сути, он делает шаблон компоновщика относительно простым для реализации за счет немного более высоких затрат времени выполнения (дополнительные объекты + клонирование).операции + уровень косвенности в функциях доступа, которые могут или не могут быть скомпилированы).

Потенциальный вариант, который вы могли бы рассмотреть: если вы сделаете объекты-строители сами по себе неизменными, вам не нужно будет защищатьсяклонировать их.Это может быть общей победой, особенно если вы строите объекты гораздо чаще, чем меняете строителей.

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