Неизменяемые бобы в Java - PullRequest
5 голосов
/ 21 ноября 2008

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

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

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

Например

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;
    }
}

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

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

Ответы [ 3 ]

3 голосов
/ 21 ноября 2008

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

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

, например

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();
    }

}
2 голосов
/ 22 ноября 2008

Думаю, я бы использовал шаблон делегирования - создайте класс 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 голос
/ 21 ноября 2008

Я использую интерфейсы и приведение типов для управления изменчивостью bean-компонентов. Я не вижу веской причины усложнять свои доменные объекты такими методами, как 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);
}

Это отвечает на ваш вопрос?

Ус

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