Существуют ли какие-либо шаблоны для реализации изменяемых и неизменяемых версий объекта-держателя?
В моем приложении у меня есть несколько типов объектов- держателей, основной целью которых является хранение разнородных связанных данных. Их жизненные циклы можно разделить на две части:
- Сбор данных, как только они становятся доступными;
- Предоставление доступа только для чтения к хранимым данным на всю оставшуюся жизнь владельца.
Очень заманчиво сделать их неизменяемыми, но данные недоступны для передачи конструктору. Самый простой обходной путь, который я вижу, состоит в том, чтобы сделать две версии класса держателя, одна изменяемая, а другая нет:
public class MutableHolder {
public int field1;
public String field2;
// ...
public Object fieldN;
}
public class Holder {
public final int field1;
public final String field2;
// ...
public final Object fieldN;
public Holder(MutableHolder mutableHolder) {
this.field1 = mutableHolder.field1;
this.field2 = mutableHolder.field2;
// ...
this.fieldN = mutableHolder.fieldN;
}
}
Тем не менее, я чувствую, что этот подход нарушает принцип DRY (я не должен забывать обновить поля обоих классов, а также конструктор, если я хочу что-то изменить) и подвержен ошибкам. Итак, вот мой вопрос: существуют ли какие-либо шаблоны, о которых я не знаю, для реализации как изменяемой, так и неизменяемой версий объекта-держателя?
редактировать
Я неожиданно обнаружил, что приведенный выше код является очень простой версией шаблона Builder (см. Это или это). Это заставляет меня думать, что в этом случае СУХОЕ нарушение считается допустимым.
2 ответа
Я тоже спотыкался об этом какое-то время. Вот шаблон дизайна, который я придумал, которого я не видел в другом месте.
public class Bob
{
// member variables
private int value;
// simple constructor
private Bob()
{
value(0);
}
// constructor with value
private Bob(int value)
{
value(value);
}
// get value
public final int value()
{
return this.value;
}
// set value
private final void value(int value)
{
this.value = value;
}
// mutable class modifies base class
public static class Mutable extends Bob
{
// simple constructor
private Mutable()
{
super();
}
// constructor with value
private Mutable(int value)
{
super(value);
}
// set value
public final void value(int value)
{
super.value(value);
}
}
// factory creator for immutable
public static final Bob immutable(int value)
{
return new Bob(value);
}
// factory creator for mutable
public static final Mutable mutable()
{
return new Mutable();
}
// another mutable factory creator
public static final Mutable mutable(int value)
{
return new Mutable(value);
}
}
- Класс не является окончательным, подклассы должны быть безопасными.
- Все конструкторы должны быть приватными.
- публичные участники должны быть окончательными.
- Частные мутаторы в базовом классе должны быть окончательными.
- публичные мутаторы в классе Mutable должны быть окончательными.
- Используйте фабричные методы для создания неизменяемых и изменяемых объектов.
Это похоже на изменяемый шаблон подкласса, но поскольку класс Mutable является внутренним классом, он имеет доступ к закрытым полям и методам базового класса, поэтому не нужно защищать ничего, что могло бы быть переопределено. Базовый класс похож на стандартный изменяемый класс, за исключением того, что конструкторы и мутаторы являются закрытыми. Изменяемый подкласс - это тонкий слой (каждый метод супер...), который раскрывает мутаторы.
Чтобы создать неизменный экземпляр: Bob test1 = Bob.immutable(99); Чтобы создать изменяемый экземпляр: Bob.Mutable test2 = Bob.mutable();
Другим возможным решением является Государственный шаблон проектирования. Так как это основная концепция, это изменение поведения объекта при изменении его состояния. Также позволяет объекту изменять свое поведение при изменении его внутреннего состояния. Объект появится, чтобы изменить свой класс.
В вашем случае вы можете рассмотреть следующую реализацию:
//maintains an instance of a ConcreteState subclass that defines the current state
public class Holder {
//your code
}
//encapsulating the behavior associated with a particular state of the Holder
public abstract class State{
//your code
}
//implements a behavior associated with a state of Holder
public class MutableHolderState extends State {
//your code
}
//implements a behavior associated with a state of Holder
public class ImmutableHolderState extends State {
//your code
}