Clean Code with Swing and Scala
I guess everybody who knows Java and Swing also knows the Swing Tutorial. It is a great source of information if you want to learn Swing. It is also a major catastrophy when it comes to structuring code. The problem is: Lots of people miss critical information contained in the tutorial like ‘Everything concerned with Swing must happen in the EDT’ but they seem to suck up the messy way to structure code like a sponge.
This might result in code like the one below. It is actually Scala code, but that shouldn’t matter much. The only piece that is a little special are the calls to Binder, which is a little Swing Binding Framework which I introduced a couple of weeks ago.
-
val panel = new JPanel()
-
val layout = new GridBagLayout()
-
val c = new GridBagConstraints()
-
c.gridx = 0
-
c.gridy = 0
-
-
panel.setLayout(layout)
-
-
c.fill = 0
-
panel.add(new JLabel("firstname"), c)
-
-
c.fill = 1
-
c.gridx += 1
-
c.weightx = 1
-
val firstnameTF = new JTextField()
-
panel.add(firstnameTF, c)
-
Binder.bind(p.firstname, firstnameTF)
-
-
c.fill = 0
-
c.weightx = 0
-
c.gridx = 0
-
c.gridy += 1
-
-
panel.add(new JLabel("lastname"), c)
-
c.fill = 1
-
-
c.gridx += 1
-
val lastnameTF = new JTextField()
-
Binder.bind(p.lastname, lastnameTF)
-
panel.add(lastnameTF, c)
-
c.fill = 0
-
c.weightx = 1
-
c.gridy += 1
-
c.anchor = GridBagConstraints.SOUTHEAST
-
val button = new JButton("save")
-
Binder.bind(p.save, button)
-
panel.add(button, c)
-
panel
-
}
What is so bad about this piece of crap … ähm … code?
The method is long. 38 lines is about 10 times longer than healthy for a method.
There is tons of code duplication.
The method does lots of different things: creating components, adding them to a panel, configuring the layout.
There is a strong dependency on the order of commands. We can’t just move stuff up or down in the method and still hope the result will be something reasonable, even if we stick to rearrangements allowed by the compiler.
All this together makes the method extremely hard to understand. How long does it take to understand what kind of GUI results? Don’t bother to much, I help. It looks like this:
and if you resize it, it looks like this
Arguably the result looks just as ugly as the code, but once the code is clean we might be able to improve on the visual design as well.
If you don’t see it in the code, you might see it in the images: There are three different ways JComponents are handled by the method: JLabels are in the left column and don’t resize. The JTextFields are in the right column and do resize and the JButton doesn’t resize and is in the buttom right. In the code this is completely hidden in the manipulation of the GridBagConstraint. So lets make it explicit in the code:
-
panel : JPanel,
-
component : JComponent,
-
row : Int) {
-
val c = new GridBagConstraints()
-
c.gridx = 0
-
c.gridy = row
-
c.weightx = 0
-
c.fill = 0
-
panel.add(component, c)
-
}
-
-
private def addToComponentColumn(
-
panel : JPanel,
-
component : JComponent,
-
row : Int) {
-
val c = new GridBagConstraints()
-
c.gridx = 1
-
c.gridy = row
-
c.weightx = 1
-
c.fill = 1
-
panel.add(component, c)
-
}
-
-
private def addButton(
-
panel : JPanel,
-
component : JButton,
-
row : Int) {
-
val c = new GridBagConstraints()
-
-
c.weightx = 1
-
c.gridx = 1
-
c.gridy = row
-
c.fill = 0
-
c.anchor = GridBagConstraints.SOUTHEAST
-
panel.add(component, c)
-
}
-
-
private def createPersonPanel(p : PersonEditor) = {
-
val panel = new JPanel()
-
val layout = new GridBagLayout()
-
panel.setLayout(layout)
-
-
addToLabelColumn(panel, new JLabel("firstname"), 0)
-
-
val firstnameTF = new JTextField()
-
Binder.bind(p.firstname, firstnameTF)
-
addToComponentColumn(panel, firstnameTF, 0)
-
-
addToLabelColumn(panel, new JLabel("lastname"), 1)
-
-
val lastnameTF = new JTextField()
-
Binder.bind(p.lastname, lastnameTF)
-
addToComponentColumn(panel, lastnameTF, 1)
-
-
val button = new JButton("save")
-
Binder.bind(p.save, button)
-
addButton(panel, button, 2)
-
panel
-
}
I introduced three methods. One for adding a JLabel, one for adding a JComponent and one for adding JButtons. These handle the arrangement of components on a JPanel. The total length of the code increased because we create the GridBagConstraints insided the methods and have to set all properties and don’t rely anymore on the previous step to leave the constraint in a specific state.
If we now look at the createPersonPanel we’ll get a strong fealing of repetition in various places:
- each call to add* methods takes the same JPanel as an argument. We can improve on this by creating a Builder wich crates the panel, contains the add* methods and can return the fully configured panel at the end.
- for each property we create a JLabel, a JTextField, bind the property to the later and add both to the JPanel. We can fix this by encapsulating it in a seperate method.
The result might look like this:
-
case class PanelBuilder() {
-
val panel = new JPanel()
-
val layout = new GridBagLayout()
-
panel.setLayout(layout)
-
-
def add(components : (JLabel, JComponent), row : Int) {
-
addToLabelColumn(components._1, row)
-
addToComponentColumn(components._2, row)
-
}
-
-
def add(
-
component : JButton,
-
row : Int) {
-
val c = new GridBagConstraints()
-
-
c.weightx = 1
-
c.gridx = 1
-
c.gridy = row
-
c.fill = 0
-
c.anchor = GridBagConstraints.SOUTHEAST
-
panel.add(component, c)
-
}
-
-
private def addToLabelColumn(
-
component : JComponent,
-
row : Int) {
-
val c = new GridBagConstraints()
-
c.gridx = 0
-
c.gridy = row
-
c.weightx = 0
-
c.fill = 0
-
panel.add(component, c)
-
}
-
-
private def addToComponentColumn(
-
component : JComponent,
-
row : Int) {
-
val c = new GridBagConstraints()
-
c.gridx = 1
-
c.gridy = row
-
c.weightx = 1
-
c.fill = 1
-
panel.add(component, c)
-
}
-
-
}
-
-
private def create(name : String, property : Property[String]) : (JLabel, JComponent) = {
-
val textField = new JTextField()
-
Binder.bind(property, textField)
-
(new JLabel(name), textField)
-
}
-
-
private def create(name : String, action : => Unit) = {
-
val button = new JButton("save")
-
Binder.bind(action, button)
-
button
-
}
-
-
private def createPersonPanel(p : PersonEditor) = {
-
val builder = PanelBuilder()
-
-
builder.add(create("firstname", p.firstname), 0)
-
builder.add(create("lastname", p.lastname), 1)
-
builder.add(create("save", p.save), 2)
-
-
builder.panel
-
}
The PanelBuilder has now two simple public add methods. In order to imitate the method signitures in Java we would have to create a couple of helper classes and interfaces. It would make the code less compact but this shouldn’t be a serious problem. Creation of the various components is just as the binding extracted in two create Methods. The createPersonPanel has now only 5 lines of code. Adding another property to the form should be trivial. Changing the extremely simplistic layout should be trivial and is at least limited to a single small class. I think this is pretty much OK for a first step toward clean swing code. So I leave it like it is right now. Although I do have further plans for this.
I hope most of you agree that the code is much easier to understand and maintain in the form it is right now. But is it really worth the effort? Some might say no. If the whole application would consist of only this little panel. I would agree. But a Swing application typically does not have a single panel with two textfields and a button. But tens or even hundreds of panels. Many consisting of large collections of components. If you have to maintain such a monster, would you prefer createPersonPanel methods like the last one, or would you prefer the first version? What if the customer if finally fed up with your crappy layout and insists on proper spacing between the labels and the JTextFields or other components?
All this is pretty nice when you start a new Swing application. But what if you have an existing Swing application? One with convoluted code just as the Swing Tutorial taught you? Well … start changing it now. Don’t sit down for two months and rewrite all your code, but find pieces of code duplication and extract them. It will be a long way, but you wont reach the end if you don’t start walking.
But what if you are not using Swing, but writing a web application? Well it really shouldn’t matter much. Your PanelBuilder might be written in JavaScript, or create HTML, but the principle is the same: Seperate creation of components, layout of components and binding of components to properties and actions.








Hi,
I think basic problem is GridBagLayout which is very verbose. Here is similar form with MigLayout. Text fields are growing when window is resized, labels are alligned to right
val firstName = new JTextField
val lastName = new JTextField
val panel = new JPanel{
setLayout(new MigLayout(“fill”))
add(new JLabel(“First name:”,”align right”)
add(firstName, “growx,wrap”)
add(new JLabel(“Last name:”,” align right)
add(lastName, “growx,wrap”)
add(new JButton(“Submit”),”spanx, align right”);
}
I agree that GridBagLayout is verbose and I do recommend and use something different (JGoodies form layout) in most cases.
BUT I don’t think it is at the root of the problem. The code you provide does two things: creating components and arranging them. Which makes the code hard to test and maintain. You have code duplication (2x new JTextField, 2x align right, 2x growx …) and you don’t even have a way to extract the values from the textfields and stuff them into a proper object.
LOL. There is also code triplication in ‘val’
Anyway, had you consider joining Scala-swing project? I think you have very similar mind set as it’s creators.
I’m quite serious about the code duplication. Of course it’s not much of an issue when you are dealing with two controls and a button, but that changes rapidly once you have ten controls on each panel and hundred of panels in the application. Even more which controls which need more work to set up than Textboxes.
I’m actually not so much impressed with what I have seen of the swing project. The wrappers they provide are nice and less verbose than normal Java/Swing but other then verbosity they don’t solve much of a problem.
Or did I miss something? or they might start working on stuff like Binding or a SCalable Rich Application Platform …
Well this style is working for me pretty well. I prefer forms (commit, reset) and events rather than binding. And my app actually _has_ GUI tests.
I have something like ‘Scala RCP’. But it is just bunch of utility classes for forms, model manipulations, resource injections etc…
Scala-Swing did think about creating binding, but it never took of
This is really well done, and certain can apply for any JVM language (including Java itself).
I’m currently doing a Swing based project, and am looking to develop a DSL for end-users as part of the implementation. This will be definitely be an inspiration. Thanks!
“What is so bad about this piece of crap … ähm … code?”
First, you are using GridBagLayout. There are a lot of useful layouts out there which can be used much easier, like TableLayout. (Btw, why is everyone now using MigLayout?)
Seconds, you are writing a GUI yourself? Didn’t you heard of GUI-Builders like Jigloo or WindowBuilder? Try to build a complex GUI without a GUI-Builder, let’s see how much time you are wasting to get the layout right by hand.
Third, if you really have to do it by hand, why not using a DSL like SwingBuilder in Groovy? Then your code would look something like that:
new SwingBuilder().edt {
frame(title: “Java Frame”, size: [100, 100], locationRelativeTo: null, show: true) {
gridLayout(cols: 1, rows: 2)
label(text: bind(source: model, sourceProperty: “count”, converter: { v -> v? “Clicked $v times”: ”}))
button(“Click me!”, actionPerformed: { model.count++ })
}
}
@Erwin Different Layouts or DSLs for building GUIs fix the problem of verbosity. But they don’t do anything about the underlying problem: Mixing of different responsibilities.
GUI Builders are nice if you want to build 1 Panel. They are ok if you have 5 similar panel. But they are a maintenance nightmare if you have 200 frames where every frame needs to behave in the same way.
The latter is the kind of application I tend to build. Of course your mileage might vary.
[...] Jens Schauder blogged about creating Swing-based user interfaces with Scala. [...]
Hi Jens
Would you be interested in joining DZone’s MVB Program (see dzone.com/aboutmvb for details)?
I think you’d be a great addition to the program. If you’re interested, send me an email and we can discuss further.
Regards
James
Hello Jens,
I also urge you to analyze your code again and take a look at the GridBagConstraints wrapper. Thus, you’ll make a simple method call chain and , of course, will sprinkle your code with a new freshness and purity. Here’s how you can make it on Java -> https://gist.github.com/1062950
I’m still studying Scala, therefore, cannot implement this on Scala, yet.
Hi Alexander,
I had a quick look at the wrapper. To be honest I wouldn’t try to fix GridBagLayout or try to make it nicer. I think it is broken beyond repair.
This of course means in turn that this article is not about hiding the problems of GridBagLayout. It is about hiding the technical details of the layout, by wrapping any manipulation of the Layout in classes and methods that operate on an abstraction level you might discuss with your user.
Actually I replaced the GridBagLayout with JGoodies FormLayout just two days ago. If you are interested, you can take a look here: https://github.com/schauder/More-Than-Properties/blob/master/src/main/scala/de/schauderhaft/mtp/PersonClient.scala
Thanks for noting Jens.
Totally agree. It’s broken and some of the bugs still doesn’t fixed (like a resizing bug). I have no idea why people still using GridBagLayout.
Scala Swing is very interesting part and could be a total fix for many of the Java Swing current problems, one of them is abstraction. Studying…