Вопрос

Мне очень любопытно узнать о возможности обеспечения неизменности для java beans (под beans здесь я подразумеваю классы с пустым конструктором, предоставляющим геттеры и сеттеры для членов).Очевидно, что эти классы не являются неизменяемыми, и там, где они используются для передачи значений с уровня данных, это кажется реальной проблемой.

Один из подходов к этой проблеме был упомянут здесь, в StackOverflow, под названием "Шаблон неизменяемого объекта в C #", где объект замораживается после полной сборки.У меня есть альтернативный подход, и я действительно хотел бы услышать мнения людей по этому поводу.

Шаблон включает в себя два класса Immutable и Mutable, где Mutable и Immutable оба реализуют интерфейс, который предоставляет немутирующие методы bean.

Например

public interface DateBean {
    public Date getDate();
    public DateBean getImmutableInstance();
    public DateBean getMutableInstance();
}

public class ImmutableDate implements DateBean {
    private Date date;

ImmutableDate(Date date) {
    this.date = new Date(date.getTime());
}

public Date getDate() {
    return new Date(date.getTime());
}

    public DateBean getImmutableInstance() {
        return this;
    }

    public DateBean getMutableInstance() {
        MutableDate dateBean = new MutableDate();
        dateBean.setDate(getDate());
        return dateBean;
    }
}

public class MutableDate implements DateBean {
    private Date date;

public Date getDate() {
    return date;
}

public void setDate(Date date) {
    this.date = date;
}

public DateBean getImmutableInstance() {
    return new ImmutableDate(this.date);
}

    public DateBean getMutableInstance() {
        MutableDate dateBean = new MutableDate();
        dateBean.setDate(getDate());
        return dateBean;
    }
}

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

Мне очень интересно услышать подход других людей к этому вопросу.(Приношу свои извинения за то, что не задал хорошего вопроса, на который можно ответить, а не обсуждать :)

Это было полезно?

Решение

Думаю, я бы использовал шаблон делегирования - создайте класс ImmutableDate с одним членом DateBean, который должен быть указан в конструкторе:

public class ImmutableDate implements DateBean
{
   private DateBean delegate;

   public ImmutableDate(DateBean d)
   {
      this.delegate = d;
   }

   public Date getDate()
   {
      return delegate.getDate();
   }
}

Если мне когда-либо понадобится установить неизменность в элементе DateBean d, я просто добавлю в него новую ImmutableDate (d). Я мог бы быть умным и быть уверенным, что не делегировал делегата, но вы поняли идею. Это позволяет избежать проблемы клиента, пытающегося превратить его во что-то изменчивое. Это очень похоже на то, как это делает JDK с Collections.unmodifiableMap () и т. Д. (Однако в этих случаях функции мутации все же необходимо реализовать и кодировать, чтобы генерировать исключение времени выполнения. Намного проще, если у вас есть базовый интерфейс без мутаторов).

И опять-таки это утомительный шаблонный код, но это такая вещь, которую хорошая IDE, такая как Eclipse, может автоматически сгенерировать для вас всего несколькими щелчками мыши.

Если вы делаете то же самое со многими объектами домена, вы можете рассмотреть возможность использования динамических прокси или, возможно, даже AOP. Тогда было бы относительно легко создать прокси для любого объекта, делегировав все методы get и перехватывая или игнорируя установленные методы соответствующим образом.

Другие советы

Некоторые комментарии (не обязательно проблемы):

  1. Класс Date сам по себе является изменяемым, поэтому вы правильно копируете его, чтобы защитить неизменность, но лично я предпочитаю преобразовывать в long в конструкторе и возвращать новую дату (longValue) в получателе.
  2. Оба ваших метода getWhateverInstance() возвращают DateBean, что потребует приведения, возможно, было бы неплохо изменить интерфейс, чтобы вместо этого возвращать определенный тип.
  3. Сказав все это, я был бы склонен просто иметь два класса, один изменяемый и один неизменяемый, разделяющий общий (т.е.получить только) интерфейс, если это уместно.Если вы думаете, что будет много преобразований взад и вперед, то добавьте конструктор копирования в оба класса.
  4. Я предпочитаю, чтобы неизменяемые классы объявляли поля как Финал чтобы заставить компилятор также обеспечивать неизменность.

например ,

public interface DateBean {
    public Date getDate();
}

public class ImmutableDate implements DateBean {
    private final long date;

    ImmutableDate(long date) {
       this.date = date;
    }

    ImmutableDate(Date date) {
       this(date.getTime());
    }

    ImmutableDate(DateBean bean) {
       this(bean.getDate());
    }

    public Date getDate() {
         return new Date(date);
    }
}


public class MutableDate implements DateBean {
    private long date;

    MutableDate() {}

    MutableDate(long date) {
       this.date = date;
    }

    MutableDate(Date date) {
       this(date.getTime());
    }

    MutableDate(DateBean bean) {
       this(bean.getDate());
    }

    public Date getDate() {
        return new Date(date);
    }

    public void setDate(Date date) {
        this.date = date.getTime();
    }

}

Я использую интерфейсы и приведение для управления изменчивостью компонентов.Я не вижу веской причины усложнять объекты моего домена такими методами, как getImmutableInstance() и getMutableInstance().

Почему бы просто не использовать наследование и абстракцию?например ,

public interface User{

  long getId();

  String getName();

  int getAge();

}

public interface MutableUser extends User{

  void setName(String name);

  void setAge(int age);

}

Вот что будет делать клиент кода:

public void validateUser(User user){
  if(user.getName() == null) ...
}

public void updateUserAge(MutableUser user, int age){
  user.setAge(age);
}

Отвечает ли это на ваш вопрос?

yc

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top