2008-11-20 9 views
5

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

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

Образец включает в себя два класса Неизменяемый и 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; 
    } 
} 

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

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

ответ

2

Я думаю, что я хотел бы использовать шаблон делегирования - сделать класс 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 и улавливать или игнорировать установленные методы, если это необходимо.

3

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

  1. Класс Дата сам изменчиво, так что вы правильно скопировать его защищайте неизменность, но лично я предпочитаю преобразовать в конструктор long и возвращать новую дату (longValue) в getter.
  2. Оба метода getWhateverInstance() возвращают 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(); 
    } 

} 
+0

Спасибо за ваши комментарии. Я согласен с тем, что конструктор копирования является существенным улучшением по сравнению с заводскими методами интерфейса. Замечание о конечных членах хорошо взято. Приветствия. – 2008-11-21 00:44:52

1

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

это ответ на ваш вопрос?

Ус

+0

Я предполагаю, что вы имеете в виду интерфейс MutableUser для расширения пользовательского интерфейса? Излишне говорить, что это все еще требует конкретного класса. Нижняя сторона заключается в том, что он открывает вам «неизменяемый», изменяемый изменчивым простым произведением, что, скорее всего, не то, что вы хотите. – 2008-11-21 00:48:45

+0

Хотя верно, что пользовательский интерфейс не предоставляет методы для изменения объекта User, который на самом деле не является гарантией неизменности, как я хотел бы просить об этом шаблоне. – 2008-11-21 00:51:01