I am trying to grasp MVP (like so many) and although there are quite a few resources out there, I'm not sure I really get it.
I am trying to do this without frameworks to really see what's going on.
Here's my code:
main method (only method in a special class called Main
):
SwingUtilities.invokeLater(() -> {
JFrame mainWindow = new JFrame();
mainWindow = new JFrame("MainFenster");
mainWindow.setSize(500, 500);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setLocationRelativeTo(null);
mainWindow.setLayout(new BorderLayout());
LoginView loginView = new SwingLoginView(mainWindow);
LoginModel loginModel = new LoginModelImpl();
LoginService loginService = new LoginServiceImpl(loginModel);
LoginPresenter presenter = new LoginPresenter(loginView, loginService);
loginView.setPresenter(presenter);
mainWindow.setVisible(true);
});
View interfaces
public interface View {
// Not sure what woudl go here, but just in case...
}
public interface LoginView extends View {
void setErrorMessage(String errorMessage);
void setNotificationMessage(String message);
void navigateToHome(); // Should go here?
void setPresenter(LoginPresenter presenter);
interface LoginViewEventListener {
void loginButtonClicked(String username, String password);
}
}
public class SwingLoginView implements LoginView {
private JFrame mainFrame;
private LoginPresenter presenter;
private JTextArea errorMessage;
private JTextArea password;
private JTextArea username;
private JTextArea notificationMessage;
private JPanel panel;
public SwingLoginView(JFrame mainWindow) {
this.mainFrame = mainWindow;
inititialize();
}
private void inititialize() {
initializeComponents();
}
private void initializeComponents() {
errorMessage = new JTextArea();
errorMessage.setText("Hello");
notificationMessage = new JTextArea();
notificationMessage.setText("UU");
password = new JTextArea();
password.setText("password");
username = new JTextArea();
username.setText("username");
JButton loginButton = new JButton("Press");
loginButton.addActionListener((e) -> {
// Could validation go here?
presenter.loginButtonClicked(username.getText(), password.getText());
});
panel = new JPanel();
panel.add(loginButton);
panel.add(errorMessage);
panel.add(notificationMessage);
panel.add(password);
panel.add(username);
mainFrame.add(panel, BorderLayout.NORTH);
}
@Override
public void setErrorMessage(String errorMessage) {
this.errorMessage.setText(errorMessage);
}
@Override
public void setNotificationMessage(String message) {
this.notificationMessage.setText(message);
}
@Override
public void navigateToHome() {
// TODO Auto-generated method stub
}
@Override
public void setPresenter(LoginPresenter presenter) {
this.presenter = presenter;
}
}
Presenter interface and class
public class LoginPresenter implements Presenter, LoginView.LoginViewEventListener {
private LoginView referenceToLoginView;
private LoginService referenceToLoginService;
public LoginPresenter(LoginView referenceToLoginView, LoginService loginService) {
this.referenceToLoginView = referenceToLoginView;
this.referenceToLoginService = loginService;
}
@Override
public void loginButtonClicked(String username, String password) {
System.out.println("presenter called");
if (username.equals("") || password.equals("")) {
this.referenceToLoginView.setErrorMessage("Password and or username empty");
return; // OK?
}
LoginModel loginModel = referenceToLoginService.login(username, password);
referenceToLoginView.setNotificationMessage("NEU");
}
@Override
public View getView() {
return referenceToLoginView;
}
}
and the model just has getters and setters for the username and password
And here's an attempt at a unit test:
@RunWith(MockitoJUnitRunner.class)
public class LoginTest {
@Mock
private LoginView mockLoginView;
@Mock
private LoginService mockLoginService;
private LoginPresenter loginPresenter;
@Before
public void setUp() {
loginPresenter = new LoginPresenter(mockLoginView, mockLoginService);
mockLoginView.setPresenter(loginPresenter);
}
@Test
public void loginShouldFailIfCredentialsAreEmpty() {
loginPresenter.loginButtonClicked("", "");
Mockito.verify(mockLoginView).setErrorMessage("Password and or username empty");
}
}
So... is this going in the right direction?
Some more concrete questions are:
- Should the view provide getter methods? Or should it pass the values to the presenter?
- Should the view interface have a navigation method? Or should navigation be totally controlled by the presenter?
- All my views need a reference to the main window / root pane or whatever, right?
- Can I include small validity checks (empty username/password etc) in the view? Or should this go in the presenter, like in my case?
- Should the presenter have knowledge of the model or should there be a service layer, like in my code? So should the login-method of my service return just a boolean to check if the login failed or a model-instance, like in my code?
- What is meant by "the model is the business logic"? I thought the model was merely the java representation of a database table?
This is really tough for me to grasp.