Неизменность после внедрения зависимости, инициализация - PullRequest
6 голосов
/ 26 мая 2010

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

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

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

Мысли

Ответы [ 5 ]

4 голосов
/ 26 мая 2010

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

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

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

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

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

3 голосов
/ 26 мая 2010

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

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

1 голос
/ 26 мая 2010

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

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

0 голосов
/ 26 мая 2010

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

Вы можете увидеть Javadoc здесь .

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

@ Александр:

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

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

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

0 голосов
/ 26 мая 2010

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

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

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

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

...