Существует несколько различных способов инициализации сложных объектов (с внедренными зависимостями и необходимой настройкой внедренных элементов), все они кажутся разумными, но имеют различные преимущества и недостатки. Я приведу конкретный пример:
final class MyClass {
private final Dependency dependency;
@Inject public MyClass(Dependency dependency) {
this.dependency = dependency;
dependency.addHandler(new Handler() {
@Override void handle(int foo) { MyClass.this.doSomething(foo); }
});
doSomething(0);
}
private void doSomething(int foo) { dependency.doSomethingElse(foo+1); }
}
Как видите, конструктор делает 3 вещи, включая вызов метода экземпляра. Мне сказали, что вызов методов экземпляра из конструктора небезопасен, потому что он обходит проверки компилятором неинициализированных членов. То есть Я мог бы позвонить doSomething(0)
до установки this.dependency
, что скомпилировало бы, но не работало. Каков наилучший способ рефакторинга?
Сделать doSomething
статичным и явно передать зависимость? В моем случае у меня есть три метода экземпляра и три поля-члена, которые все зависят друг от друга, так что кажется, что это лишний шаблон, делающий все эти три статичными.
Переместите addHandler
и doSomething
в метод @Inject public void init()
. В то время как использование с Guice будет прозрачным, требуется, чтобы любая ручная конструкция вызывала init()
, иначе объект не будет полностью функциональным, если кто-то забудет. Кроме того, это раскрывает больше API, оба из которых кажутся плохими идеями.
Оберните вложенный класс, чтобы сохранить зависимость, чтобы убедиться, что он ведет себя правильно, без предоставления дополнительного API:
class DependencyManager {
private final Dependency dependency;
public DependecyManager(Dependency dependency) { ... }
public doSomething(int foo) { ... }
}
@Inject public MyClass(Dependency dependency) {
DependencyManager manager = new DependencyManager(dependency);
manager.doSomething(0);
}
Это извлекает методы экземпляра из всех конструкторов, но создает дополнительный слой классов, и когда у меня уже были внутренние и анонимные классы (например, этот обработчик), это может сбить с толку - когда я попробовал это, мне сказали переместить DependencyManager
отдельный файл, который также неприятен, потому что теперь это несколько файлов, чтобы сделать одну вещь.
Так какой же предпочтительный способ справиться с подобной ситуацией?