I am trying to abstract some functionality from my desktop application (JavaFX) for changing views.
So, what I am thinking is:
Navigator interface with methods like goToAccountsOverview()
, goToAccountDetails(Account account)
etc. The implementation will depend on a ViewFactory
to create the Views and a DisplayingViewChanger
to change the view that is being displayed.
There will be multiple navigator interfaces depending on the state of the application, starting with:
NotConnectedStateNavigator
(goToConnectToDatabase()
,goToCreateDatabase()
etc)ConnectedStateNavigator
(goToAccountsOverview()
,goToAccountDetails(Account account)
etc)
So each state will have its own navigator and view factory and a common view changer. The view factories and navigators will not share a common interface (createView(ViewEnum v)
and goToView(View view)
respectively).
While implementing this I run into a problem. An example of the problem is this:
In the AccountsOverviewView
there is a table with all the accounts. Each account has a button to view the details. Pressing that button should navigate to the AccountDetailsView
with the ViewModel of the account (I am using some sort of MVVM).
To do that it would call connectedStateNavigator.goToAccountDetails(accountVM)
which would call viewChanger.changeView(connectedStateViewFactory.createAccountDetailsView(accountVM)
).
So the AccountsOverviewView
needs the ConnectedStateNavigator
, which needs the ConnectedStateViewFactory
, which needs to be able to construct the original AccountsOverviewView
, which needs the ConnectedStateNavigator
etc.
Some code to explain it better:
public class AccountsOverviewView {
public AccountsOverviewView(ConnectedStateNavigator navigator, ...) {
(...)
accountDetailsButton.onClick(e -> navigator.goToAccountDetails(account));
(...)
}
}
public class ConnectedStateNavigator {
public ConnectedStateNavigator(ConnectedStateViewFactory viewFactory, ...) { ... }
public void goToAccountDetails(Account account) {
viewChanger.changeView(viewFactory.createAccountDetailsView(account));
}
}
public class ConnectedStateViewFactory {
public ConnectedStateViewFactory(ConnectedStateNavigator navigator, ...) { ... }
public View createAccountsOverviewView() {
return new AccountsOverviewView(new AccountsOverviewViewModel(navigator, ...));
}
public View createAccountDetailsView(Account account) { ... }
}
How could I design this better to avoid this cyclic reference? Is using a setter or an asynchronous event system the only solution?
ConnectedStateNavigator
and replace theviewFactory
parameter with a lambda that takes in a navigator, calls the constructor, and returns an instance of the factory (Function<ConnectedStateNavigator,ConnectedStateViewFactory> factoryCreator
). You'd then just doviewFactory = factoryCreator.apply(this)
in the body.