Неизменяемость после внедрения зависимостей, инициализация

Я хотел бы иметь возможность указать, что переменные-члены объекта являются неизменяемыми после того, как объект был «инициализирован», что для меня означает после того, как он был введен с любыми зависимостями и выполнил любые другие операции инициализации, которые он может выполнять только после ДИ.

Существуют ли языки, которые удовлетворяют мой интерес — которые формализуют DI, инициализацию и поддерживают неизменность таким образом? Может быть, глупо делать их частью языка; возможно, нет. Я не уверен.

Сегодня я программирую на Java, но я не могу использовать "final" почти так часто, как хотелось бы, потому что эти фазы происходят после завершения выполнения конструктора. Любые советы о том, как получить то, что я хочу с Java? Я предполагаю, что мои объекты могли бы реализовать базовый класс, чтобы эти фазы происходили до завершения конструктора, или использовать аспекты, чтобы сделать то же самое.

Мысли?


person Ladlestein    schedule 25.05.2010    source источник


Ответы (5)


Я думаю, это зависит от того, что вы хотите от неизменности. Если вам нужна гарантированная потокобезопасность (где все должно быть объявлено окончательным, включая зависимости), то я думаю, что внедрение фабрики, построителя или конструктора - ваши единственные варианты.

Однако, если вы просто хотите неизменности состояния, то объявления переменных состояния final должно быть достаточно. Даже неизменяемый класс String имеет в своей реализации изменяемое поле (кэш значения хэш-кода). Пока ваш код гарантирует, что экземпляр недоступен без инъекции, все должно быть хорошо.

person Yishai    schedule 25.05.2010
comment
Я просто хочу, чтобы было ясно, какие элементы изменяемы, а какие нет. Странно, но я никогда не задумывался о том, что инъекция сеттера несовместима с неизменяемым состоянием. Ну, я не думал об этом, когда писал вопрос, по крайней мере :-) - person Ladlestein; 26.05.2010

Существует два основных способа создания неизменяемых объектов:

  1. используйте шаблон построителя/фабрики - построитель может быть изменяемым, но создаваемые им объекты неизменяемы, обычно реализуются с конечными полями. Вы также можете комбинировать их, поэтому сам объект используется для создания новых экземпляров, обычно с помощью методов «мутатора», которые изменяют состояние в отдельном новом экземпляре. Spring FactoryBean является примером этого.

  2. создайте подкласс MutableObject, который поддерживает флаг изменяемого состояния. Все ваши мутаторы проверяют изменяемое состояние перед внесением каких-либо изменений — если объект был установлен как неизменяемый, то проверка выдает исключение, в противном случае изменение продолжается.

Первый подход полностью ориентирован на Spring, поскольку требует реализации интерфейса, специфичного для Spring. Вы можете создавать фабричные bean-компоненты, которые являются обычными bean-компонентами, с помощью атрибутов factory-method/factory-bean в bean-компоненте, что удаляет пружинную зависимость из вашего кода.

Использование второго подхода особенно полезно для Spring. Вы можете указать Spring вызывать метод после инициализации bean-компонента, например. seal(), который запечатывает объект - делает его неизменяемым. В качестве альтернативы вы можете реализовать небольшой BeanFactoryPostProcessor, чтобы делать это автоматически, не забывая устанавливать init-method="seal". на каждом неизменяемом компоненте.

person mdma    schedule 25.05.2010
comment
Ваш ответ полезен; Я выбрал другой, потому что в нем говорилось о внедрении конструктора, что немного лучше кристаллизовало проблему для меня. - person Ladlestein; 26.05.2010
comment
Я хочу использовать final или, на другом языке, любой другой механизм, означающий и обеспечивающий неизменность. Действительно ли мне помогает Builder в Java? Разве использование конструктора не необходимо для использования final? - person Ladlestein; 26.05.2010
comment
Построитель может использовать внедрение сеттера и создает объект с помощью внедрения конструктора. ср. большинство реализаций FactoryBean в Spring — они настраиваются с помощью внедрения сеттера. - person mdma; 26.05.2010

В Java можно использовать конструктор. для инициализации неизменяемого объекта в его конструкторе, поэтому вы будете избегать сеттеров.

Однако если вы используете Scala, неизменяемость по умолчанию.

person Jordão    schedule 25.05.2010
comment
Как я уже сказал выше, чем помогает Builder, мне кажется, что Builder не актуален, если только я не использую внедрение конструктора. - person Ladlestein; 26.05.2010
comment
Именно поэтому я и сказал, что нужно избегать сеттеров. - person Jordão; 26.05.2010

В Java, если вы каким-то образом используете методы-мутаторы для выполнения своих настроек, довольно дешево (хотя и довольно уродливо в моих глазах) добавить логику для предотвращения изменений после инициализации объекта.

public void setMyProperty(String newValue) {
   checkInitialized();
   myProperty = newValue;
}

public void checkInitialized() {
   if ( initialized ) {
      throw new IllegalStateException("Setter called after initialization");
   }
}

В лучшем случае это динамическая проверка. Это не дает вам никакой статической обратной связи по сравнению с тем, что у вас уже было.

person Mark Peters    schedule 25.05.2010

Чтобы указать, что класс является неизменным, вы можете использовать аннотацию @Immutable.

Документацию по Java можно посмотреть здесь.

Это хорошо работает с плагином Findbugs в Eclipse.

@Александр:

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

Фрагмент из Javadoc:

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

person Espen    schedule 25.05.2010