After reading the series of posts from Gregg Bolinger about Swing best practices, I thought I should share some of the patterns I use when writing Swing applications. I was going to post this as a comment in the original post, but it was too long so I'm writing a post instead ...
Usually we have only one main frame in a typical Swing application, so I implement this as a singleton named MainFrame :
public class MainFrame extends JFrame {
private static MainFrame INSTANCE;
public static MainFrame getInstance() {
if (INSTANCE == null)
INSTANCE = new MainFrame();
return INSTANCE;
}
private MainFrame() {
// ...
}
}
Each components in the frame has an accessor, where it is lazily loaded. For example :
private JPanel footer;
private JList menu;
public JPanel getFooter() {
if (footer == null) {
footer = new JPanel();
// ...
}
return footer;
}
public JList getMenu() {
if (menu == null) {
menu = new JList();
// ...
}
return menu;
}
So if somewhere in the application I have to do some stuff on a component all I have to do is :
MainFrame.getInstance().getMyComponent().doSomeStuff();
These components are added to the frame inside the constructor :
private MainFrame() {
// ...
this.add(getFooter());
// ...
}
If the various listeners are too complex to be created using an anonymous class, I use a private inner class named InnerListener :
private class InnerListener extends MouseAdapter implements MouseMotionListener, WindowListener {
public void mouseClicked(MouseEvent e) {
// ...
}
// ...
}
which is registered inside the constructor :
private InnerListener innerListener;
private MainFrame() {
// ...
innerListener = new InnerListener();
this.getFooter().addMouseListener(innerListener);
// ...
}
The idea here is to keep the various methods from the listeners interface private. See this example from my "fake app" :
before, no inner class
after, using a private inner class
The public API of the main frame is clean, no useless methods are accessible. Of course you can create multiple inner listeners if there is a lot of code, for example MouseInnerListener and WindowInnerListener ...
The problem with these patterns is that the MainFrame class can become quite big (878 lines in my fake app, and still growing).
The complete skeleton for the MainFrame class :
public class MainFrame extends JFrame {
private static MainFrame INSTANCE;
private JPanel footer;
private JList menu;
private InnerListener innerListener;
public static MainFrame getInstance() {
if (INSTANCE == null)
INSTANCE = new MainFrame();
return INSTANCE;
}
private MainFrame() {
// ...
this.add(getFooter());
// ...
innerListener = new InnerListener();
this.getFooter().addMouseListener(innerListener);
// ...
}
public JPanel getFooter() {
if (footer == null) {
footer = new JPanel();
// ...
}
return footer;
}
public JList getMenu() {
if (menu == null) {
menu = new JList();
// ...
}
return menu;
}
private class InnerListener extends MouseAdapter implements MouseMotionListener, WindowListener {
public void mouseClicked(MouseEvent e) {
// ...
}
// ...
}
}
Of courses these are not "official" rules, just some practices I have found over the time, so I'd really like some feedback :)


8 comments:
Hi,
personally I don't think using a singleton is a very good practice in general; I try to avoid it at all costs (hint: quite easy if you use Dependency Injection).
Moreover, I tend to avoid lazy evaluation (take a look at Josh Bloch "Effective Java") if it's not absolutely necessary: it adds a lot of boilerplate code for no added value. For all my panels, I define all the components private final and create them when they are declared. In addition, I never add any accessor (getter) to them, I consider these components should be invisible to classes outside the panel.
Finally, what I generally avoid is to add components directly to a JFrame, JDialog or JInternalFrame. In my experience, it is much better to declare panels classes (that derive JPanel) including all components and then add them to a JDialog, JFrame...
More on that on the Maddie project itself when I have some time to participate more.
Last point: I have big problems displaying your blog correctly (IE7 and FF3): all code snippets are much bigger than needed (in particular, they include loads of extra blank lines).
Oh I almost forgot, thanks for many of your previous posts (I just read a few this morning, they are quite interesting).
Cheers
I've created swing apps the way you describe. For my next swing app I'm planning to use a simplistic pluggin architecture as in this article:
http://www.javaranch.com/journal/200607/Journal200607.jsp#a1
Cheers!
Interesting design pattern. I generally do something similar but not quite the same. I'm looking forward to your participation on Project Maddie if you get the chance.
@Jean-Francois Poilpret
While most of us(or lots of us) read Effective Java, and agree that singletons can add boilerplate code, it's still very used and not always that dramatic.
Main windows are often provided as singletons(80% of the time). It is not necessarily "that bad"
- when you don't abuse singletons
- and ensure correct initialization and thread safety.
When sigletons are not used, there's often a helper class with static methods to lookup services and/or GUI components.
Yes, one could use an IOC container such as Spring, overkill for smaller applications??
Yves Zoundi
XPontus XML Editor http://xpontus.sf.net
VFSJFileChooser http://vfsjfilechooser.sf.net
@Jean-François : About lazy evaluation : I should have also said that I not only create the component in the getter, but also initialize it, for example :
public JList getList() {
if (list == null) {
list = new JList(new MetaItemListModel());
list.setSelectedIndex(0);
list.setPreferredSize(new Dimension(100, 300));
list.setCellRenderer( ... );
list.addListSelectionListener( ... );
...
}
return list;
}
This way, there is not an big bunch of code in a single method which create and initialize all the components.
Thx for your comments, I will look at the problem with the code snippets !
@Gregg : I'm afraid I do not have the necessary time to participate in a project, but thanks for the invitation :)
@Yves : helper class with static methods to lookup services and/or GUI components sound like a good solution too. I also love your advices at http://yveszoundi.blogspot.com/2008/09/drinking-huge-cup-of-java.html too !
@Yves:
Without using Spring, there are other (simpler) ways to use DI.
The fact that many developers do that does not make it a best practice... I have seen loads of cluttered Swing applications with "main frame" class of several thousands lines of code, because it was more than just a main frame, it looked like a provider for almost any service that any other part of the application would need. Finally the code was cluttered with instance refs to this Main Frame, almost impossible to refactor...
The problem with "simple" GUI applications is that they don't stay simple very long, and this is where good practices help you, whereas bad ones kill your productivity.
One principle I always try to keep in mind in designing GUI is to separate concerns: my main frame is just a panel container like any other!
@Christophe:
That's a matter of personal preferences than. I prefer having all the initialization code for all widgets in one location (or sometimes I split per kind of initialization: one method for behavior -listeners- init of widgets, one for look init...)
What I particularly don't catch in your getter methods is why they are public: why do you give the possibility to any class in all your application to access all widgets in the main frame?
@Jean-François,
you're right, the getXXX() could be private or a least protected. The fact is that I take this code from an application which allow plugins, so these methods are public in order to allow the plugins to modify part of the UI ... But in a "classic" application there is no reason these methods should be public.
Post a Comment