2

I have written an application that is using Swing for the GUI, accepts a file via the GUI, parses the Input, saves it in DataList and sends it to a server. I'm concerned about the whole design of my program, which I don't think is very good. I'm using Netbeans to design the GUI and have a class MainClass that starts that GUI and has a static reference to the GUI. Also there are a couple of ExecClasses that do the aforementioned parsing and sending of the data.

                 +----------------------+
                 | MainClass (static)   |
                 |----------------------|
          +------+  -DataList           +-----+
          |      |                      |     |
    static|      +-+--------------+-----+     |static
  reference        |              |           |reference
          |        |new ()        | new ()    |
          |        |              |           |
          |        |              |           |
        +-+--------v----+      +--v-----------+--+
        |               |      |                 |
        | SwingGUIClass |      | ExecClasses     |
        |               |      |                 |
        +--/\-----------+      +-----------------+
           |
          Input file

Here a short overview of the MainClass :

public class MainClass {

    private static MainClass mainClass;
    private static ExecClass1 ex1;
    private static ExecClass2 ex2;
    private static ExecClass3 ex3;

  public static void startExecClass2(String param){

     ex2 = new ExecClass2(param);
  }

I'm using these references so that the SwingGUIClass can execute a method in the ExecClass1 for example. I chose this approach since I have a TextArea that needs to get the data out of one of the ExecClasses and display it in the GUI. Since I can't modify the TextArea from the ExecClass.

public class SwingGUIClass {

  [...]
  private void ButtonActionPerformed(java.awt.event.ActionEvent evt) { 

  Label.setText(MainClass.getList());
}
  private void Button2ActionPerformed(java.awt.event.ActionEvent evt) { 

   MainClass.startExecClass2(Button2.getText());
}

I understand that this is a far from great design and not following a couple of good practice guides, e.g. MVC. So my question is : How would you design this and which general pointers can you give me?

cete3
  • 647
  • 6
  • 19
  • 1
    it's a broad theme, not strictly related to Swing :-) And quite a lot of possibilities - you might want to start by looking f.i. at [Martin Fowler's series about presentation logic](http://martinfowler.com/eaaDev/OrganizingPresentations.html) BTW: please learn java naming conventions and stick to them – kleopatra Sep 25 '13 at 10:59
  • @kleopatra First of all, thanks for the link - I will look into it. Of course the code I posted here is not the actual code, I renamed the classes/variables (MainClas,ex2) in order to make it more understandable to someone that isn't familiar with my program, or are you referring to something else? – cete3 Sep 25 '13 at 11:06
  • It seems like you're missing Model objects. Not like Swing makes them easy to work with. Consider using Java FX instead which supports binding. – millimoose Sep 25 '13 at 11:26
  • @millimoose AFAIU model is the data of the program, which I have stored in a List inside the `MainClass`. I considered JavaFX, but since I wanted to be compatible with Java 6 and JavaFX 1 seems to be considered not as good as 2.0 I opted for Swing. – cete3 Sep 25 '13 at 11:29
  • @tmn29a When using databinding you typically use a "view model". This is a data structure that represents, logically, what your GUI should display at any given moment. You bind GUI components to properties of this view model. ("bind" = the GUI components will watch for change notifications from the view model and update automatically when they receive them.) Your "controller" (in your case it seems the `ExecClass`es are this) then updates the view model in response to user input. – millimoose Sep 25 '13 at 11:31
  • @tmn29a The coupling then becomes: 1. the GUI observes the view model; 2. the GUI hands user input to the controller; 3. the controller updates the view model and the actual model - the controller acts as a mediator between the two, mapping between them. (Your view model and actual model contain the same data, but represent it somewhat differently because it's used in different contexts.) As you can see, the lower layers aren't strongly aware of the upper layers. The only iffy coupling is the bidirectional one between model and view model but that's manageable since they're similar. – millimoose Sep 25 '13 at 11:36
  • @tmn29a This *can* be done in Swing but generally it's a huge pain in the ass. There is some support for this, e.g. JTable can be backed by a model that works like I outline. JLabels can't for instance. There were attempts to make a standard data-binding framework for Swing (JSR 295) but I believe they were scrapped in favour of the clean-slate approach of Java FX, so your options would be to either roll your own or use an old, barely documented, possibly broken experimental library. – millimoose Sep 25 '13 at 11:38
  • A lateral suggestion, if you're willing to consider Groovy, would be [Griffon](http://griffon.codehaus.org/) - a honest to goodness "(G)Rails-like" MVC framework for Swing. It mines the ideas of JSR-295 and JSR-296, and possibly implements them better since making a fully observable view model (a necessity for MVC/MVVM in a GUI app) is a lot of annoying boilerplate. Also, to explore MVVM on a conceptual level, I'd suggest looking at the [Knockout.js](http://learn.knockoutjs.com/) tutorial. It's not practically applicable, but it's a framework that implements **nothing else** but MVVM. – millimoose Sep 25 '13 at 11:41
  • (Also: why Java 6 compatibility? Java 6 is unsupported by Oracle since February.) – millimoose Sep 25 '13 at 11:48

1 Answers1

7

First of all, don't do logic based on static references on things, that you can use in multiple context. For example in future, you can have a requirement to have several windows of your GUI interfaces interacting with several instances of your services.

Also you are killing testability.

Treat GUI and application logic separately - don't think in your application logic (exec classes) about GUI text field etc. Just think about input and output, and provide a class to communicate each other (the controller). You can provide data to application logic in controller, get results and display it in GUI like:

public void processFile( SomeInputFromGui input ) {
 SomeResult result = applicationLogicObject.process( input );
 guiObject.showResult( result );
}

Your components should be loose coupled, so you can reuse and test them. You can achieve that with simple dependency injection like putting your dependencies in contructors/setters:

public void initApplication() {
  AppLogic logic = new AppLogic();
  AppWindow window = new AppWindow();
  AppController controller = new Controller( logic , window );
}

This is very simple draft of controller initializning method. With that, you can test/reuse your logic or your GUI in other places like unit tests.

To move bussiness logic from your window, where all events are fired (buttons etc.) you can create a interface, which will work with your window:

public interface ProcessingController {
public void processFile( File x );
public void checkIntegrity();
public SomeDataValues getCurrentDataValues();
}

And you can implement this logic in your controler (implements) and use it as GUI events receiver:

window.setProcessingController( controller );

...

private void ButtonActionPerformed(java.awt.event.ActionEvent evt) { 
   processingController.processText( jMyTextField.getText() );
}

And now you have two-way communication with window and controller.

Those are basic points, that gives you testability and ability to make as much logic/controller/windows as you want. Also you have loose coupled components: you can inject a almost-empty stub of AppLogic for test purposes or faked AppWindow to simulate user acions in test. Of course to subsitute components, you should extract interfeces and provide specific implementations:

SwingAppWindow implements ApplicationUserInterface { ...
SQLDataManager implements ApplicationDataLogic { ...
BasicController implements ProcessingController { ...

Of course you can split it even further to separate data access and bussines logic.

And remeber that all your gui actions (events, updates) should run in swing event thread, so you should use SwingUtils, beacuse swing is not thread safe:

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
      .... queued action that changes the gui ...
    }
  });

Remeber to not hard-code object instantation with new in your logic classes, for example don't make new Window and new ApplicationDataModel in your controller - because you can't test your controller independly or reuse it with different logic/window implementation - you can create some class only to prepare your application dependencies (create components and link them) and "start it" - it's generally called a factory.

If your logic will grown and become more complicated, split it into more service objects and you can use a command pattern to produce commands in your gui and process it (for example in thread safe queue) in application services - which also will be good starting point to undo/redo ability.

Last one thing - if you have any long running processing task (even if it took 1 second we can say that it's long running), remeber that invoking it directly or in swingUtils will freeze your gui, so for lenghty operations create separate threads with a Thread, Executors, Runnable, SwingWorker or something (you can use a observer pattern to monitor progress etc).

Have in mind that this is really a big topic and this post mention only some small general advices.

The "other road" to take can be to use a already provided architecture to create GUI application like Eclipse RCP or Netbeans Platform.

Piotr Müller
  • 5,323
  • 5
  • 55
  • 82
  • Wow, first of all, thanks for that very elaborate answer. You make a lot of points that I also had been on my mind. One point that was bugging me is, that if I want to separate the GUI and the logic I have to write a lot of boilerplate code. So for example I would need a separate "getter/setter" for each TextField. This feels very redundant or am I missing something ? – cete3 Sep 25 '13 at 11:24
  • You can transfer a "DTO" - data transfer object from your model to view by controller, wich will have a combination of some data reperesentation (several texts to display etc). and just pass this single object. You can also do some mapping of your data to your gui, wich will have direct access to your data model and listen to changed/refresh events and update your gui (see swing models like ListModel, TableModel etc. with implementations like DefaultListModel, DefaultTableModel - such examples stores data itself but you can lookup for data in model. – Piotr Müller Sep 25 '13 at 11:46
  • Also in such model when doing lookup you can make a thread-safe "copy of state" when requesting data from model. This will prevent errors of displaing incosistient data when it changes during gui repainting. So on demand or data change event, such mapping model requests current state of data from model, caches it (to secure from modification and incosistiency) and updates gui components. – Piotr Müller Sep 25 '13 at 11:47