11

I do sometimes show Dialog boxes in my Java application.

Currently the Controller classes are (expect some exceptions where only getters are called on my model) used like mediators between my model and my UI.

But my UI knows my controllers and my controllers know my UI.

Whenever I add a new dialog I add a method in a controller and in the view class.

Is there a more elegant way to extend my program with new user dialogs by using a design pattern?

To illustrate you how my interaction looks right now I will append some code snippets.

Code from my UI

    itmEmailSettings.addActionListener( new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            controller.showEmailSettingsDialog();
        }
    } );

More UI Code

    public void showEmailSettingsDialog(String host, int port, int authMode,
            String user, String pass, String fromEMail, String fromName) {
        EmailSettingsDialog d = new EmailSettingsDialog(
                host, port, authMode,
                user, pass, fromEMail, fromName
                );
        d.createJDialog( mainFrame.getFrame() ).setVisible(true);
        if(d.isValid()){
            controller.changeEmailSettings(  d.getHost(), d.getPort(), d.getAuthMode(), d.getFromEMail(), d.getFromName(), d.getUser(), d.getPass()  );
        }
    }

Controller code:

public void showEmailSettingsDialog() {
    try{
        if(!pm.hasProjectFileAccess()){
            mainFrame.showNoProjectfileAccess();
            return;
        }
        ProgrammSettingsRepository pr = Utils.getProgrammSettingsRepository(pm);
        String host = pr.retreive(ProgrammSettingsRepository.KEY_EMAIL_HOST);
        int port = pr.retreive(ProgrammSettingsRepository.KEY_EMAIL_PORT)==null?0:Integer.parseInt( pr.retreive(ProgrammSettingsRepository.KEY_EMAIL_PORT) );
        int authMode = pr.retreive(ProgrammSettingsRepository.KEY_EMAIL_SSL_MODE)==null?0:Integer.parseInt( pr.retreive(ProgrammSettingsRepository.KEY_EMAIL_SSL_MODE) );
        String user = pr.retreive(ProgrammSettingsRepository.KEY_EMAIL_USER);
        String pass = pr.retreive(ProgrammSettingsRepository.KEY_EMAIL_PASSWORD);
        String fromEMail = pr.retreive(ProgrammSettingsRepository.KEY_EMAIL_FROM_EMAIL);
        String fromName = pr.retreive(ProgrammSettingsRepository.KEY_EMAIL_FROM_NAME);

        menuView.showEmailSettingsDialog(host, port, authMode, user, pass, fromEMail, fromName);
    }catch(SQLException e){
        throw new RuntimeException(e.getMessage(), e);
    }
}

public void changeEmailSettings(String host, int port, int authMode,
        String fromEMail, String fromName, String user, String pass) {
    try {
        ProgrammSettingsRepository pr = Utils.getProgrammSettingsRepository(pm);
        pr.store( ProgrammSettingsRepository.KEY_EMAIL_HOST , String.valueOf( host ));
        pr.store( ProgrammSettingsRepository.KEY_EMAIL_PORT , String.valueOf( port ));
        pr.store( ProgrammSettingsRepository.KEY_EMAIL_SSL_MODE , String.valueOf( authMode ));
        pr.store( ProgrammSettingsRepository.KEY_EMAIL_USER , String.valueOf( user ));
        pr.store( ProgrammSettingsRepository.KEY_EMAIL_PASSWORD, String.valueOf( pass ));
        pr.store( ProgrammSettingsRepository.KEY_EMAIL_FROM_EMAIL , String.valueOf( fromEMail ));
        pr.store( ProgrammSettingsRepository.KEY_EMAIL_FROM_NAME , String.valueOf( fromName ));
        pr.store(ProgrammSettingsRepository.KEY_EMAIL_SETTINGS_CONFIGURED, "true");
    } catch (SQLException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}
Peter O.
  • 32,158
  • 14
  • 82
  • 96
Jakob Alexander Eichler
  • 2,988
  • 3
  • 33
  • 49
  • Maybe I can use a onIsValid Method for my Dialoges and a onIsInvalid Method. Those methods receive runnables from the controllers. The Controller is responsible for creating the Dialog object and sending it to the UI. Which displays the dialog and uses the callbacks. But that way the controller class still has to be changed for each dialog to be shown, what I think is okay. Every UI command results in a change in my controller classes does not it? – Jakob Alexander Eichler Jul 31 '15 at 18:58
  • 1
    I'm not sure that directly employing a recognized *design pattern* is necessary to clean this up. – crush Jul 31 '15 at 18:59
  • Good OOP might do the job as well. – Jakob Alexander Eichler Jul 31 '15 at 18:59
  • 1
    One thing that I do in JavaScript is subscribe my dialogs to a particular event. When that event is received, I open my dialog with any data that was passed to the event. When they perform some action, I emit a new event with the data attached. This is more of an asynchronous model, though. The code above is all synchronous and single-threaded. Are these requirements? – crush Jul 31 '15 at 19:01
  • Furthermore many user actions will be wrapped in command objects soon. And I think I will use getCommandXY methods in my controller. And the commands will know the controller. – Jakob Alexander Eichler Jul 31 '15 at 19:02
  • Being synchronous is not a requirement, but does not it make my life easier? – Jakob Alexander Eichler Jul 31 '15 at 19:03
  • Well, I think that you could employ a pub-sub/observer design pattern here and remain synchronous. A benefit of subscribing to events for communication is that it removes dependencies. For example, you can emit the event for a dialog to open from a button click, but if no dialog is listening for that particular event, then no dialog will open. This can be a good or bad thing. It's more useful in the sense that you might not care if anything is listening to your dialog's "accept" event, but if they are, then they can do what they need to do with the data that is emitted. – crush Jul 31 '15 at 19:07
  • 1
    Your controller shouldn't know anything about your UI, it should only provide services that have to be used (or _consumed_ in a more modern jargon) by whatever front-end is going to use them. Expose only the functionality that saves the email setting in the controller, create and additional view for your email dialog that takes the controller as a dependency (a composite) and invoke the email settings dialog from your current view passing the controller. – higuaro Jul 31 '15 at 19:09
  • In the head first design patterns book about MVC is written that the controller controls the UI, where else than in the controller can I know that I need to call a method like: "public void showNoProjectfileAccess()" How would you invoce feedback from the controller if the controller does not know the UI? – Jakob Alexander Eichler Jul 31 '15 at 19:17
  • @tokam Emitting and listening for events would decouple the controllers from the UI. You'd need to wire up the events through some common event dispatch, though. – crush Jul 31 '15 at 19:26
  • Its very well explained here (in context of your question) http://stackoverflow.com/questions/5217611/the-mvc-pattern-and-swing – Saurabh Jul 31 '15 at 19:36
  • @tokam of course controller controls the UI by through data or events not through direct manipulation of the UI elements, in that case your controllers are going to be extremely coupled with the UI and they would be just extensions of the UI. Controllers should be decoupled from views. [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) should be enough for the controller to do any _update_ on the view without knowing anything about them (yes, _them_, because a single controller could be related to _N_ different views) – higuaro Jul 31 '15 at 19:39
  • 1
    So how would you inform the view, than about a dialog that need to be shown? Passing events in the update method? Returning a complex data object from the controller to the view? – Jakob Alexander Eichler Jul 31 '15 at 19:50
  • Hard to give you a direct example without providing code at this point. What UI framework are you using? I use so many languages, I often have to refer back to the documentation to remember syntax, classes, and other mechanisms. Are you using JavaFX, Swing, something else? – crush Jul 31 '15 at 20:32

1 Answers1

3

I understand that from UI you call Controller and then you call UI from Controller to show dialog. The controller does some calculations and then call UI to show dialog.

If you implement IReportable to the UI classes.

public interface IReportable {
    public void showYesNoDialog(//all needed params);
    public void showSimpleDialog(//all needed params);
            .
            .
            .
}

public class DialogController() {
    private IRportable _reportable;//delegator

    public DialogController(IRportable _reportable) {//bridge pattern.
        _reportable = reportable;
    }

    public void showEmailDialog() {
        //calculations
        _reportable.showSimpleDialog(//params);
    }

    public void showCustomerDialog() {
        //calculations
        _reportable.showYesNoDialog(//params);
    }

}

public class UIClass implements IReportable {
    private DialogController _dialogController;

    public UIClass() {
        _dialogController = new DialogController();
    }

    public void someMethod() {
        if() {

        }
        ...
        _dialogController.showEmailDialog();
    }

    public void someOtherMethod() {
        if() {

        }
        ...
        _dialogController.showCustomerDialog();
    }

    @Override
    public void showYesNoDialog(//all needed params) {
        //code here to show dialog according to params.
    }

    @Override
    public void showSimpleDialog(//all needed params) {
        //code here to show dialog according to params.
    }

}

That way you have dependancy for the interface and not to single ui components. You can alter interface to abstract class to have the same functionality to all of the UI classes...

ddarellis
  • 3,912
  • 3
  • 25
  • 53
  • Currently I do not pass params but classes that create a concrete dialog to the UI. But there is not a real advantage I assume – Jakob Alexander Eichler Aug 14 '15 at 17:49
  • 2
    It is better to expose interfaces than class in a way to create separate plugins for each of your packages and is better to give out your interfaces and ask programmers to compliant with them. You don`t give classes you give interfaces. – ddarellis Aug 14 '15 at 22:30