Java Notes
UI-Model communication
Separate the user interface from the model
Separate the user interface from the "model". The single most important high-level structuring principle is to separate the user interface from the model (business logic, problem domain, ...). This provides a huge improvement in simplicity, altho at first it may not appear so. Program enhancements and maintenance are much easier with this structure.
Could you replace the UI? A test for whether you've achieved this separation is whether you could replace the user interface without changing the model. Without rewriting the model, can you change your program from a GUI to a command line interface, or to run with a web interface? If you can't say yes to this, perhaps your model and user interface are not well-enough separated.
Model knows nothing about user interface. One essential test is whether the model knows anything about the user interface. It must not! The user interface knows about the model, not vice-versa.
MVC? A further separation is often proposed which additionally splits the user interface into a View (displaying information) and Controller (processing user interactions) -- the well-known MVC pattern. For most applications your programs will be simpler just separating the user interface from the model - the Presentation-Model or M-VC pattern shown below.
How to communicate between a UI and the model
Assumption - quick actions. For this discussion it's assumed that the actions carried out by the model are quick (eg, no long waits for Internet accesses or long computations). The reason for this qualification is that, as long as all actions are quick, they can be carried out on the Event Dispatch Thread (EDT) that the listeners are running on. The GUI is frozen while an action is carried out on this thread, so they must be quick. If they are long, a separate thread must be started. This is not too hard, but the complications are beyond the scope of this short description.
Model object calls. The UI and model have to communicate, and the major form of communication between program elements is method calls. A good, common way this is done is to create a model object that can be used by the UI to call on the model methods. Every time the user does something in the UI, the UI listener will use that object to call appropriate methods in the model.
Errors - exceptions or return values. How does the model handle errors? Beginning programmers are often tempted to write error messages to the user. Don't do this. Instead the model should either throw an exception, which the UI can then catch, or return a value which shows that there was an error. Exceptions are often the better choice, but sometimes returning an error indication is much simpler.
Updating the UI. When something is done to the model by calls from the UI to the model, it's often necessary to update the UI.
- Direct return values. This may be simple, eg displaying the values returned from the model method calls.
- Indirect updates. It can be more complex, for example if there is a graphical view of the model,
it may be necessary to call
repaint()
, which will callpaintComponent()
, which will then interrogate the model to see what it should display. - Observer pattern - listeners. The view may be fairly complex, in which case the simplest solution can be to have each part of the view that has to be updated register a listener with the model, so that whenever the model changes, that listener will be called and the view can update itself (presumably by calling on the model to get the state that should be displayed). The ChangeListener interface provides a simple way to do this. [TODO: Show example].