please be advised, this is a long post. Sorry for that but I want to make my point clear:
I was wondering how to separate Swing GUI from Presentation and Business Logic for quite a long time. At work I had to implement a 3 MD Excel Export for some data with a small Swing Dialog to configure the export. We do not use a framework like Spring for this so I had to implement it myself.
I wanted to completely separate GUI from Business Logic, which are in precise following tasks:
of course the GUI shouldnt have notice of the BL implementation and vice versa.
I created several interfaces for all those tasks above, e. g. a ProgressListener
, LogMessageListener
, JobDoneListener
,
etc., to be fired by the Business Logic. For instance, if the Business Logic wants to tell about logging, it calls
fireLogListeners("Job has been started");
classes that implement the public interface LogListener + are attached to the BL, now will be notified about the "Job has been started" log message. All these listeners are at this time implemented by the GUI itself, which in general looks like this:
public class ExportDialog extends JDialog implements ProgressListener, LogListener, JobFinishedListener, ErrorListener {
@Override
public void jobFinished(Object result){
// Create Save File dialog and save exported Data to file.
}
@Override
public void reportProgress(int steps){
progressBar.setValue(progressBar.getValue()+steps);
}
@Override
public void errorOccured(Exception ex, String additionalMessage){
ExceptionDialog dialog = new ExceptionDialog(additionalMessage, ex);
dialog.open();
}
// etc.
}
The "GUI and BL creating class" simply attaches the GUI (as all these listeners' interface) to the BL, which looks something like this:
exportJob.addProgressListener(uiDialog);
exportJob.addLogListener(uiDialog);
exportJob.addJobFinishedListener(uiDialog);
exportJob.start();
I am now quite unsure about that because it looks weird because of all those newly created listener Interfaces. What do you think about? How do you separate your Swing GUI components from BL?
Edit: For better demonstrating purpose I created a Demo workspace in eclipse file-upload.net/download-9065013/exampleWorkspace.zip.html I pasted it to pastebin also, but better import those classes in eclipse, pretty a lot of code http://pastebin.com/LR51UmMp
A few things.
I would no have the uiDialog code in the ExportFunction class. The whole perform method should just be code in the main class. The ExportFunctions responsibility is to 'export' not to 'show gui'.
public static void main(String[] args) {
ExportFunction exporter = new ExportFunction();
final ExportUIDialog uiDialog = new ExportUIDialog();
uiDialog.addActionPerformedListener(exporter);
uiDialog.pack();
uiDialog.setVisible(true);
}
(Swing.invokeLater() is not needed)
You seem to be overengineering a fair bit. I don't know why you would expect to have many threads to be running at the same time. When you press the button, you would only expect one thread to run right? Then there would be no need to have an array of actionPerformedListener.
instead of this :
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (startConditionsFulfilled()) {
fireActionListener(ActionPerformedListener.STARTJOB);
}
}
});
why not just :
final ExportJob exportJob = new ExportJob();
exportJob.addJobFinishedListener(this);
exportJob.addLogListener(this);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
exportJob.start();
}
});
That way you can get rid of ExportFunction which doesn't really serve any purpose.
You seem to have a lot of arrays of listeners. Unless you really really need them I wouldn't bother with them and keep it as simple as possible.
Instead of :
Thread.sleep(1000);
fireLogListener("Excel Sheet 2 created");
Thread.sleep(1000);
Just have :
Thread.sleep(1000);
log("Excelt Sheet 1 created");
Thread.sleep(1000);
where log is :
private void log(final String message) {
((DefaultListModel<String>) list.getModel()).addElement(message);
}
This way you are keeping it simpler and cleaner.
GUI should not know about BL, but BL somehow has to tell the GUI what to do. You can abstract ad infinitum with lots of interfaces, but in 99.99% of applications this is not necessary, especially yours which seems fairly simple.
So while the code you have written is pretty good, i would try and simplify and reduce the interfaces. It doesn't warrant that much engineering.
Basically, your architecture seems ok to me. I suppose you wonder if it is because of the numerous listeners you set up.
A solution for this might be either:
a) to have a generic Event class, with subclasses for specific events. You could use a visitor to implement the actual listeners.
b) to use an Event Bus (see guava, for instance). With an event bus architecture, your model will publish events to the event bus, and your UI objects will listen for events from the event bus, and filter them.
Some systems can even use annotations for declaring listener methods.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With