Geertjan is a DZone Zone Leader and has posted 466 posts at DZone. You can read more from them at their website. View Full User Profile

Porting to Griffon

09.15.2008
| 28676 views |
  • submit to reddit

The Controller

The controller is where ALL the processing takes place. All of it takes place within a single file called "AnagramGrameGriffonController.groovy". First of all, we need to put the original processing code there. This is the original code that figures out whether the user's entered code correctly unscrambled the given word:

private static final String[] WORD_LIST = {
"abstraction",
"arithmetic",
"traditional"};

private static final String[] SCRAMBLED_WORD_LIST = {
"batsartcoin",
"ratimhteci",
"rtdatioialn"
};

public String getWord(int idx) {
return WORD_LIST[idx];
}

public String getScrambledWord(int idx) {
return SCRAMBLED_WORD_LIST[idx];
}

public int getSize() {
return WORD_LIST.length;
}

public boolean isCorrect(int idx, String userGuess) {
return userGuess.equals(getWord(idx));
}

Note: The actual application has many more words (i.e., more than the 3 you see above), but I removed the majority of them to save space.

And here is the Groovified version of the above:

def WORD_LIST = [
"abstraction",
"arithmetic",
"traditional"]

def SCRAMBLED_WORD_LIST = [
"batsartcoin",
"ratimhteci",
"rtdatioialn"
]

def getWord(int idx) {
WORD_LIST.get(idx);
}

def getScrambledWord(int idx) {
SCRAMBLED_WORD_LIST.get(idx);
}

def getSize() {
WORD_LIST.size;
}

def isCorrect(int idx, String userGuess) {
userGuess.equals(getWord(idx));
}

A second area of processing that the controller is concerned with is the action events. Remember how the buttons and menu items in the View file referred to descriptions in the Actions file, which in turn referred to the controller? Well, here are the action events, all within the controller. Take special note of the references to the model (such as "model.feedback"), because here is where the model is changed which, because of the "bind" attributes pointed out in the previous section, will then result in the view automatically being updated, thus creating a loosely coupled view/controller relationship:

def guessWord = { evt ->
if (isCorrect(wordIdx, model.guessedWord)){
model.feedback = AnagramGameGriffonModel.FEEDBACK_CORRECT
} else {
model.feedback = AnagramGameGriffonModel.FEEDBACK_INCORRECT
}
view.guessedWord.text = ""
}

def nextWord = { evt ->
wordIdx = (wordIdx + 1) % getSize()
model.scrambledWord = getScrambledWord(wordIdx)
model.feedback = AnagramGameGriffonModel.FEEDBACK_NONE
}

def exit = { evt ->
app.shutdown()
}

def about = { evt ->
showDialog("aboutDialog")
}

private void showDialog( dialogName ) {
def dialog = view."$dialogName"
if( dialog.visible ) return
dialog.pack()
int x = app.appFrames[0].x + (app.appFrames[0].width - dialog.width) / 2
int y = app.appFrames[0].y + (app.appFrames[0].height - dialog.height) / 2
dialog.setLocation(x, y)
dialog.show()
}

Isn't it cool that the action events are in the same file as the related processing code? In the original Java application, this is what the "guessedWordActionPerformed" looked like:

private void guessedWordActionPerformed(java.awt.event.ActionEvent evt)
{
if (wordLibrary.isCorrect(wordIdx, guessedWord.getText())){
feedbackLabel.setText("Correct! Try a new word!");
getRootPane().setDefaultButton(nextTrial);
} else {
feedbackLabel.setText("Incorrect! Try again!");
guessedWord.setText("");
}
guessedWord.requestFocusInWindow();
}

Notice how "isCorrect" is called on "wordLibrary" above. No longer is this necessary and no longer is the view and the controller as intertwined as it is above, in retrospect.

Another thing to notice is how the "showDialog" method above (lines 24 to 32, two snippets ago) works. Look at it. It receives a dialog name and then opens the related dialog in the view. The related dialog name is the id of the dialog, defined in this case as "aboutDialog". So, the same "showDialog" method could be used for every dialog in the application, i.e., you would use it wherever applicable, simply passing in the id of the dialog you'd like the "showDialog" method to show for you. But how does the "showDialog" method find the About box? Or any other dialog? In fact, how is the whole application initialized? That's the final element of the controller that hasn't been touched on yet. Similar to the approach taken in "Flying with Griffon", the controller exposes an entry point (and wiring to the other parts of the MVC triad) like this:

// these will be injected by Griffon
def model
def view

def wordIdx = 0

def loadPages() {
// called inside EDT
model.scrambledWord = getScrambledWord(wordIdx)
}

The "loadPages()" could be called anything at all. However, the "Startup.groovy" file, which is one of the application's lifecycle files, is as follows, i.e., is used to initialize the whole application, as well as to add the "AnagramGameGriffonDialogs" file to the mix:

def rootBuilder = app.builders.root
def rootController = app.controllers.root
def rootModel = app.models.root

rootBuilder.build(AnagramGameGriffonDialogs)
rootController.loadPages()

And that's all that happens in the controller. In fact, everything happens in the controller, which is exactly how it should be.

AttachmentSize
fig-1.png11.5 KB
fig-2.png11.43 KB
fig-3.png58.63 KB
fig-4.png11.42 KB
Published at DZone with permission of its author, Geertjan Wielenga.