Index
Elsewhere
Navigation
« Why So Many TV Shows Suck | Main | Adventures with Scala and Vaadin - Part 3 »
Wednesday
Jul012009

Adventures with Scala and Vaadin - Part 4

Firstly, I'd like to thank all the people who've said nice things about the first three parts - here, on twitter and on various news-groups. Of special note is Mark Harrah, the creator of SBT. Not only did he drop me a nice note, he made a change to SBT so that the issue whereby you couldn't usefully run jetty in batch mode is solved. Now that's service!

I'd also like to apologise to the people who've been reading my blog on the web. I realise that I need to change to a variable width column layout so that you can actually see all of the code. I'll get to it soon... honest.

Last time I promised I'd talk about continuous compilation and deployment. This is pretty complex, so try to keep up.

Normally to compile all the code and run jetty you'd type:


~/code/spike : sbt
> jetty-restart


Or some such. To get continuous compilation and deployment going you need to do this:


~/code/spike : sbt
> ~ jetty-restart


See the tilde on the second line? That's it. That sets up file watchers to watch for changed files/resources and executes the specified action when it detects them

Want to run your tests every time the code changes?


~/code/spike : sbt
> ~ test


I'm sorry if you'd like an XML configuration file or something similar. We're all out of complexity this evening, try again tomorrow.

Back to the Scala & Vaadin.

The next example, from page 40 of the Book of Vaadin is slightly more substantial. I'm hoping that Scala will let us make even bigger improvements to the code because we've more to work with.

The Java code:


public class WindowOpener extends CustomComponent implements Window.CloseListener {
Window mainwindow;
Window mywindow;
Button openbutton;
Button closebutton;
Label explanation;

public WindowOpener(String label, Window main) {
mainwindow = main;
final VerticalLayout layout = new VerticalLayout();
openbutton = new Button("Open Window", this, "openButtonClick");
explanation = new Label("Explanation");
layout.addComponent(openbutton);
layout.addComponent(explanation);
setCompositionRoot(layout);
}

public void openButtonClick(Button.ClickEvent event) {
mywindow = new Window("My Dialog");
mywindow.setPositionX(200);
mywindow.setPositionY(100);
mainwindow.addWindow(mywindow);

mywindow.addListener(this);

mywindow.addComponent(new Label("A text label in the window."));
closebutton = new Button("Close", this, "closeButtonClick");
mywindow.addComponent(closebutton);
openbutton.setEnabled(false);
explanation.setValue("Window opened");
}

public void closeButtonClick(Button.ClickEvent event) {
mainwindow.removeWindow(mywindow);
openbutton.setEnabled(true);
explanation.setValue("Closed with button");
}

public void windowClose(CloseEvent e) {
openbutton.setEnabled(true);
explanation.setValue("Closed with window controls");
}
}

public void init() {
Window main = new Window("The Main Window");
setMainWindow(main);
main.addComponent(new WindowOpener("Window Opener", main));
}


The free floating init() method is supposed to be placed into the Application class.

I've removed the comments and done a little tidying up of the structure: people have suggested that by not tidying the Java code in the earlier examples I was doing Java a disservice. I also removed the comments because I believe that comments are, for the most part, an abomination, a crutch that has removed the need for programmers to support their own weight and write clean code. As with most things, I could be wrong. I doubt it, but it is possible. I'm not egotistical enough to believe I'm never wrong. Just very, very rarely. Honest.

Transliteration:

package spike

import com.vaadin.Application
import com.vaadin.ui._

class WindowOpener(abel: String, main: Window) extends CustomComponent with Window.CloseListener {
var mainwindow: Window = null
var mywindow: Window = null
var openbutton: Button = null
var closebutton: Button = null
var explanation: Label = null
mainwindow = main
val layout = new VerticalLayout()
openbutton = new Button("Open Window", this, "openButtonClick")
explanation = new Label("Explanation")
layout.addComponent(openbutton)
layout.addComponent(explanation)
setCompositionRoot(layout)

def openButtonClick(event: Button#ClickEvent): Unit = {
mywindow = new Window("My Dialog")
mywindow.setPositionX(200)
mywindow.setPositionY(100)
mainwindow.addWindow(mywindow)

mywindow.addListener(this)

mywindow.addComponent(new Label("A text label in the window."))
closebutton = new Button("Close", this, "closeButtonClick")
mywindow.addComponent(closebutton)
openbutton.setEnabled(false)
explanation.setValue("Window opened")
}

def closeButtonClick(event: Button#ClickEvent): Unit = {
mainwindow.removeWindow(mywindow)
openbutton.setEnabled(true)
explanation.setValue("Closed with button")
}

def windowClose(e: Window#CloseEvent): Unit = {
openbutton.setEnabled(true)
explanation.setValue("Closed with window controls")
}
}

class SpikeApplication2 extends Application {
override def init: Unit = {
val main = new Window("The Main Window")
setMainWindow(main)
main.addComponent(new WindowOpener("Window Opener", main))
}
}


I've dropped this into a new file on my system src/main/scala/spike/Application2.scala.

To get it to run, you need to change the web.xml. Change the line:

<param-value>spike.SpikeApplication</param-value>

to:

<param-value>spike.SpikeApplicatio2n</param-value>

Yup, there's a 2 on the end of the class name now. Fire up the app and have a look - we're going to get pimping!

So, what do I want to change first? Well, if you look where the Buttons are instantiated you'll see something that made my skin crawl. In an earlier episode I talked about the ugly anonymous inner class usage, but mentioned that it was probably the best choice. Best of a bad lot in Java as it were. This is one of the other choices. The button is being instantiated with a target object and a method name so that the button can call the method when clicked. I know why people do this, I've done it myself and in Ruby or Smalltalk it'd be idiomatic, people would be prepared for it, and it wouldn't get messed up. In Java having method names as strings is always a bad idea. Amusingly, as if my sub-consience was trying to prove me right, I misspelled one of the method names not once, but twice. And when I ran the app.. it didn't work.

Using method names as Strings and calling them by reflection isn't a good idea. There are so many things that can go wrong - signature changes, misspellings, changes in visibility level, return type changes, exceptions etc. In Java, the belief that static typing protects you from these sort of problems means that developers just aren't prepared for it. Thankfully, the SButton class we created earlier solves this problem in a nice type-safe, statically defined way. Goodbye reflection invocation.

One of the ugly parts about using reflection is that the methods you're calling back to have to be public so that the reflector can see them. This leads to classes with odd "don't call me I'm not what you think" methods.

This class also implements the WindowListener interface because it needs to respond somehow, and some of the class' state is modified as a result of the callback. Implementing this interface here was the expedient thing to do, but it does mean that the class has a broader interface, implements an interface so that it can comply with the demands of its own internal workings and, if it wanted to listen to multiple child windows would need to jump through hoops to figure out which child window it was that was being close. I'm gonna get my closure tools out again.


package spike

import com.vaadin.Application
import com.vaadin.ui._

class SWindowCloseListener(action: Window#CloseEvent => Unit) extends Window.CloseListener {
def windowClose(event: Window#CloseEvent) = {
action(event)
}
}

class WindowOpener(private val mainWindow: Window) extends CustomComponent {
private val openbutton = new SButton("Open Window", _ => createSubWindow)
private val explanation = new Label("Explanation")

private val layout = new VerticalLayout()
layout.addComponent(openbutton)
layout.addComponent(explanation)
setCompositionRoot(layout)

private var subWindow: Window = null

private def createSubWindow: Unit = {
subWindow = new Window("My Dialog")
subWindow.setPositionX(200)
subWindow.setPositionY(100)
subWindow.addComponent(new Label("A text label in the window."))
subWindow.addComponent(new SButton("Close", _ => closeSubWindow))
subWindow.addListener(new SWindowCloseListener(_ => onSubWindowClose))
mainWindow.addWindow(subWindow)

openbutton.setEnabled(false)
explanation.setValue("Window opened")
}

private def closeSubWindow = {
mainWindow.removeWindow(subWindow)
openbutton.setEnabled(true)
explanation.setValue("Closed with button")
}

private def onSubWindowClose = {
openbutton.setEnabled(true)
explanation.setValue("Closed with window controls")
}
}

class SpikeApplication2 extends Application {
override def init = {
val main = new Window("The Main Window")
main.addComponent(new WindowOpener(main))
setMainWindow(main)
}
}


In the tidy up, we've gone from five instance variables down to four ( three of them immutable values), lost a constructor parameter that didn't do anything, created a nice little reusable listener class, removed risky reflection invocation and removed ALL of the new public methods and properties. The interface of our class is now identical to the interface of our super class.

References (8)

References allow you to track sources for this article, as well as articles that were written in response to this article.
  • Response
    Response: www.qb.se
    From what source do veterans discover online desires?
  • Response
    NFL is definitely 1 of the greatest sports in America. It has a major following.
  • Response
    Response: diets
    Rob Lally : Robert Lally : Renaissance Technologist - Blog - Adventures with Scala and Vaadin - Part 4
  • Response
    Response: web page
    Rob Lally : Robert Lally : Renaissance Technologist - Blog - Adventures with Scala and Vaadin - Part 4
  • Response
    Rob Lally : Robert Lally : Renaissance Technologist - Blog - Adventures with Scala and Vaadin - Part 4
  • Response
    Rob Lally : Robert Lally : Renaissance Technologist - Blog - Adventures with Scala and Vaadin - Part 4
  • Response
    Response: pubic hair removal
    Rob Lally : Robert Lally : Renaissance Technologist - Blog - Adventures with Scala and Vaadin - Part 4
  • Response
    Rob Lally : Robert Lally : Renaissance Technologist - Blog - Adventures with Scala and Vaadin - Part 4

Reader Comments (4)

This series is turning out great! Looking forward to the next part.
Hopefully the end result will be a 'scala bindings' project in the incubator ;-)

About the listeners, this is something I've been lobbying for, and there is actually a ticket: http://dev.vaadin.com/ticket/2490
This is definitely the worst kind of listener. Almost as bad, for other reasons, is having 'this' implement the listener interface, exposing it to the world - as in new Button("Ok", this).
What we have left is an inner class listener { new Button("Ok", new OkListener(); } and private variable holding an anonymous listener { new Button("Ok", okListener); }.
Yeah, they're all bad in comparison - verbose, if not conceptually flawed.

Best Regards,
Marc

July 2, 2009 | Unregistered CommenterMarc ENglund

Hi, I was curious about this:

"I also removed the comments because I believe that comments are, for the most part, an abomination, a crutch that has removed the need for programmers to support their own weight and write clean code."

Are you serious? Comments may be the most important parts of code. They are, de facto, part of the code. Think about the JavaDocs for Swing. Swing is not thread-safe, but nothing in the code spells that out except the comments.

I have lost days and days of work trying to understand the intent of some code that was not working correctly, where if someone had just taken two minutes to write some comments then I would have known what the code was supposed to do and I could have fixed it quickly.

A comment like yours makes it sound like you don't work with others, which probably isn't true. So let me try to explain this way: people don't always write code correctly, but at least comments in the code can be "correct" so that someone else down the line has a chance of maintaining things.

I agree with you totally that people should write clean, understandable code. But sometimes the best-chosen field and method names don't go as far as a couple sentences saying what some code is supposed to do and, MUCH more importantly, what it isn't supposed to do.

To complete the circuit: poorly-written, well-documented code, is still poor code.

Cheers,
Bobby

July 2, 2009 | Unregistered CommenterBobby

Creating a set of full-fledged Scala bindings for Vaadin is something I'd love to do. I just hope I can find the time, my role at onedrum.com is keeping me busy now and it is only going to get hotter.

Still, I'm digging the combination however it works out.

Your point about the different possible types of listeners is spot-on. Java only gives you so many choices and they're all either cumbersome or flawed. My mind flip-flops between which is the worst. I hope I don't come across as having been critical of the developers - they're in a tough situation and they've done the best they could and given people choices.

July 2, 2009 | Unregistered Commenterrob

Bobby,

In many code-bases comments can be the most valuable part of the code. That's because people wrote comments rather than clear code. If people didn't have comments they'd be compelled to write clearer code.

I don't believe that you're really suggesting that the fact that Swing is not thread safe is something that should be communicated by a comment? Which file should I put that in? Line 293 of JContainer sound like a good place?

Comments are not documentation. They're a crutch where people can describe what they intended the code to do - quite often what they intended the code to do when they first wrote it. It doesn't always describe what it did when they were finished. It even less often reflects what the code does after two years. Automated refactoring tools have made this even more true.

You say you've lost days and days trying to understand the intent of code that wasn't working correctly. Comments that describe the implementation wouldn't help. Comments that describe the intent of the writer would only help sometimes. They wrote code that doesn't work, what's the chances they wrote a comment that accurately described their intent?

The English language is vague and imprecise. If you can't describe your intent in a structured, type-safe programming language why should I believe you can do it in free-form prose?

I'm not opposed to comments entirely - that's why I put 'mostly' in the sentence. There are occasions when a comment can be really helpful. Not many, but a few times.

When you look at code-bases that are heavily commented, often the majority of lines are comments. This means you can see less of the code and have to scroll around more. Is this sort of JavaDoc comments actually helpful?

/**
* Adds two strings
* @param string1 a String
* @param string2 another String
* @return a String
*/
public String addTwoStrings(String string1, String string2) {
return string1 + string1;
}

Not at all from my perspective. Two thirds of that code is comment. There is no new information at all in those comments. It is in fact duplication - you've said the same thing twice. And as any engineer will tell you, writing the same thing in two places is asking for problems.

You're right, I've always worked in teams. And I can categorically state that banning comments and demanding clean code leads to a much higher quality of code. I've worked on code-bases that have been handed off to other teams that have had a ratio of one line of comment per thousand lines of code and the team who took over the project had no problems picking things up.

I'm not being confrontational, but have you ever tried building a multi-year, multi-developer project and insisting on readable code without comments. I have and it works really, really well. I've done it repeatedly with different teams and I'm always happy with the results. So are the developers who work on them. It takes a while for people to adjust, but once they do they never want to go back.

I think there's some ground we both agree on well documented code but poor code is still poor code. I'd rather have well documented poor code than poor code alone. But I'd rather have clear code WITHOUT comments than clear code WITH comments because at that point the comments add more cost than the value they bring.

Overall, my point is that comments directly lead to poor code. Comments make it easier to get away with writing unclear code.

Thanks for the comment, I really appreciate you taking the time to talk and talk in a civilised manner. Good man!

R.

July 2, 2009 | Unregistered Commenterrob

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
All HTML will be escaped. Hyperlinks will be created for URLs automatically.