Я создаю настольное приложение JavaFX с использованием шаблона MVVM. Каждый View
требует ViewModel
. Вместо использования ViewA viewA = new ViewA(new ViewModelA());
я решил абстрагировать его, используя фабричный шаблон.
Конструктор ViewFactory имеет аргументы для других служб, которые нужны моделям представления. Например, часть моего приложения показывает все коллекции изображений, хранящиеся в базе данных, соответствующие CollectionsView
и CollectionsViewModel
. Чтобы извлечь все изображения и коллекции из базы данных, я создал CollectionService
, который передается в ViewFactory.
код:
public interface CollectionService {
List<ImageCollectionItem> getAllImagesForCollection(Collection collection);
List<Collection> getAllCollections();
}
public class CollectionsView extends View<CollectionsViewModel> {
public CollectionsView(CollectionsViewModel model) { ... }
}
public class CollectionsViewModel implements ViewModel {
public CollectionsViewModel(CollectionService collectionService) {
collections.set(collectionService.getAllCollections());
}
}
public class ViewFactory {
public ViewFactory(CollectionService collectionService) { ... }
public CollectionsView createCollectionsView() {
return new CollectionsView(new CollectionsViewModel(collectionService));
}
}
Пока все отлично. Проблема начинается с другого View
, ConnectView
, который в основном представляет собой экран выбора базы данных с кнопкой подключения внизу. Когда кнопка подключения нажата, вид должен измениться на основной, скажем, CollectionsView
для простоты.
Чтобы немного абстрагироваться, я создал интерфейс ConnectionManager
(я знаю, что имя не самое лучшее, но я не мог придумать лучшего) новое подключение к базе данных установлено и передано соответствующий экземпляр SQL Connection
.
код:
public class ConnectView { ... }
public class ConnectViewModel {
public ConnectViewModel(ConnectionManager connectionManager, ...)
public onConnectPress() {
// check database exists, valid schema, etc
// create connection
connectionManager.connected(newConnection);
}
}
public interface ConnectionManager {
void connected(Connection connection);
}
Теперь в реализации ConnectionManager
, когда вызывается connected
, создаются службы, которым требуется база данных, и представление на экране меняется на CollectionsView
.
Для этого я создал новый класс с именем NavigationService
, который создает представление с использованием ViewFactory
и устанавливает его как тот, что на экране.
код:
public class NavigationService {
public NavigationService(ViewFactory viewFactory, ...) { ... }
public void goToCollectionsView() {
CollectionsView colView = viewFactory.createCollectionsView();
// set colView on screen
}
}
ConnectionManager connectionManagerImpl = connection -> {
// create services that need connection (CollectionService, ...)
navigator.goToCollectionsView();
}
Опять все работает хорошо. Но что, если у меня есть дополнительный View
, который не требует подключенного состояния? Может быть, кнопка помощи на экране подключения, которая должна создать HelpView
? Как вернуться на экран подключения? Чтобы добавить goToConnectView()
в навигатор, я должен передать ему ConnectionManager
, чтобы создать представление, которое, в свою очередь, требует навигатор.
Итак, чтобы создать ConnectionManager
, мне нужен NavigatorService
и наоборот.
Решения, о которых я думал:
- Создайте менеджер соединений без навигатора, создайте навигатор, а затем просто используйте установщик.
- Создайте менеджер соединений без навигатора, создайте навигатор и используйте шаблон слушателя для вызова навигатора при установлении нового соединения.
- Разделите навигатор на
ConnectedStateNavigatorService
и NotConnectedStateNavigator
, что, в свою очередь, требует разделения фабрики просмотра на ConnectedStateViewFactory
и NotConnectedStateViewFactory
.
Мне не нравится вариант 1, потому что я не хочу зависеть от дополнительного утверждения, просто кажется грязным.
Мне тоже не нравится вариант 2, потому что такой вид выполнения со слушателями, событиями и т. Д. Кажется мне более подходящим для C #, а не для правильного выбора для Java. И снова, я должен положиться на дополнительный addListener
звонок.
Хотя мне не нравится вариант 3, я выбрал именно этот. Но опять же, это просто не правильно. Код начинает загромождаться сервисами, фабриками, которые я кодировал как интерфейсы в моем приложении, а это означает, что для реализации каждого из них существует дополнительный класс.
Мой вопрос: как вы думаете, это нормально?
Примечание: я не хочу использовать фреймворк mvvmFX, потому что мне не нравится вся магия, а также слишком много полагаюсь на рефлексию.
(извините за мой английский, но я не являюсь носителем языка)