Red Squirrel Reflections
Dave Hoover explores the psychology of software development

Dave Hoover
dave.hoover@gmail.com

Categories
All [Atom]
Craftsmanship [Atom]
Dynamic [Atom]
Intersection [Atom]
Learning [Atom]
Links [Atom]
Polyglot [Atom]
Projects [Atom]
XP [Atom]
Old Blog

Obtivian Blogs

Andy Maleh
Colin Harris
Fred Polgardy
Jim Breen
Kevin Taylor
Todd Webb
Turner King
Tyler Jennings

Archives

March 2009 (1)
January 2009 (1)
December 2008 (1)
October 2008 (3)
September 2008 (1)
June 2008 (4)
April 2008 (3)
March 2008 (1)
February 2008 (1)
August 2007 (1)
July 2007 (1)
June 2007 (1)
May 2007 (4)
April 2007 (3)
March 2007 (5)
February 2007 (6)
January 2007 (6)
December 2006 (10)
November 2006 (5)
October 2006 (8)
September 2006 (8)
August 2006 (5)
July 2006 (12)
June 2006 (7)
May 2006 (5)
April 2006 (5)
March 2006 (4)
February 2006 (2)
January 2006 (5)
December 2005 (5)
November 2005 (3)
October 2005 (3)
September 2005 (6)
August 2005 (4)
July 2005 (7)
June 2005 (14)
May 2005 (6)
April 2005 (8)
March 2005 (9)
February 2005 (11)
January 2005 (16)
Old Archives

 

Sat, 22 Jan 2005

Quote Manager Project -- Day 6

An ongoing pet project blog...

I've added multiplie author fields to the interface. Here is the ugly screen...

And the code that produced it...

package com.redsquirrel.quotes.gui;

import ...

public class QuoteManagerPanel extends JPanel {
    private static final int NUMBER_OF_AUTHORS = 6;
    ...
    private final JTextField[] authorUrls = new JTextField[NUMBER_OF_AUTHORS];
    private final JTextField[] authorFirstNames = new JTextField[NUMBER_OF_AUTHORS];
    private final JTextField[] authorLastNames = new JTextField[NUMBER_OF_AUTHORS];
    ...

    public QuoteManagerPanel() {
        setLayout(new GridBagLayout());

        for (int i = 0; i < NUMBER_OF_AUTHORS; i++) {
            authorUrls[i] = new JTextField(15);
            authorFirstNames[i] = new JTextField(7);
            authorLastNames[i] = new JTextField(7);
        }

        addButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Source source = new Source(QuoteManagerPanel.this.source.getText(), page.getText(), isbn.getText());
                Collection<Person> authors = new ArrayList<Person>();

                for (int i = 0; i < NUMBER_OF_AUTHORS; i++)
                    if (authorFirstNames[i].getText() != "")
                        authors.add(new Person(authorFirstNames[i].getText(), authorLastNames[i].getText(), authorUrls[i].getText()));

                Person quoter = new Person(quoterFirstName.getText(), quoterLastName.getText(), quoterUrl.getText());
                new Quote(quoteText.getText(), source, authors, quoter);
            }
        });

        layoutForm();
    }

    private void layoutForm() {
        ...
    }
}
Dave the customer is feeling a bit better now that I can enter the authors I need, but Dave the developer is not happy...still no tests to speak of. Let's take a quick look around and find some easy wins... How about that anonymous ActionListener? I'm going to try to test it...

I've brought up my test template in IDEA and I have a blank test method...

package com.redsquirrel.com.quotes.gui;

import org.jmock.MockObjectTestCase;

public class AddButtonActionListenerTest extends MockObjectTestCase {
    public void testShould() {

    }
}
So how do I test this thing? Currently it is coupled to all of the JTextComponents in QuoteManagerPanel. The code that I'm most nervous about is the for loop with the if condition. I'm going to need to promote this ActionListener to a concrete class, but I don't want to pass in ten objects to the constructor...or call ten different setters. How about I pull the related fields into their own JPanels? The thought occurred to me when I had to create three separate author arrays.

I'm pulling into the station. I'll check in and extract the fields into JPanels on the way home...

I'll start with the code that handles authors' JTextComponents. Here's it is in its current glory (in bold):

    private void layoutForm() {
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;

        int y = 0;
        addLabel("Quote", constraints, y++);
        addField(quoteText, constraints);
        addLabel("Source", constraints, y++);
        addField(source, constraints);
        addLabel("ISBN", constraints, y++);
        addField(isbn, constraints);
        addLabel("Page", constraints, y++);
        addField(page, constraints);

        for (int i = 0; i < NUMBER_OF_AUTHORS; i++) {
            addLabel("Author " + (i+1), constraints, y++);
            addFullName(authorFirstNames[i], authorLastNames[i], constraints);
            addLabel("URL", constraints, y++);
            addField(authorUrls[i], constraints);
        }

        addLabel("Quoter", constraints, y++);
        addFullName(quoterFirstName, quoterLastName, constraints);
        addLabel("URL", constraints, y++);
        addField(quoterUrl, constraints);

        constraints.anchor = GridBagConstraints.EAST;
        constraints.gridx = 0;
        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridy = y++;
        add(addButton, constraints);
    }
Time to extract method...
    private void layoutForm() {
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;

        int y = 0;
        addLabel("Quote", constraints, y++);
        addField(quoteText, constraints);
        addLabel("Source", constraints, y++);
        addField(source, constraints);
        addLabel("ISBN", constraints, y++);
        addField(isbn, constraints);
        addLabel("Page", constraints, y++);
        addField(page, constraints);

        for (int i = 0; i < NUMBER_OF_AUTHORS; i++) {
            layoutAuthor(i, constraints, y);
            y += 2;
        }

        addLabel("Quoter", constraints, y++);
        addFullName(quoterFirstName, quoterLastName, constraints);
        addLabel("URL", constraints, y++);
        addField(quoterUrl, constraints);

        constraints.anchor = GridBagConstraints.EAST;
        constraints.gridx = 0;
        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridy = y++;
        add(addButton, constraints);
    }

    private void layoutAuthor(int i, GridBagConstraints constraints, int y) {
        addLabel("Author " + (i+1), constraints, y);
        addFullName(authorFirstNames[i], authorLastNames[i], constraints);
        addLabel("URL", constraints, y+1);
        addField(authorUrls[i], constraints);
    }
It's a start. But I need to group the fields into a JPanel. I'll start with inlining the methods inside layoutAuthor...
    private void layoutAuthor(int i, GridBagConstraints constraints, int y) {
        constraints.gridx = 0;
        constraints.gridwidth = 1;
        constraints.gridy = y;
        add(new JLabel("Author " + (i+1)), constraints);

        constraints.gridwidth = GridBagConstraints.RELATIVE;
        constraints.gridx = 1;
        add(authorFirstNames[i], constraints);

        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridx = 2;
        add(authorLastNames[i], constraints);

        constraints.gridx = 0;
        constraints.gridwidth = 1;
        constraints.gridy = y+1;
        add(new JLabel("URL"), constraints);

        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridx = 1;
        add(authorUrls[i], constraints);
    }
Bletch! That's ugly. Let's introduce that JPanel ASAP...
    private void layoutForm() {
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;

        int y = 0;
        addLabel("Quote", constraints, y++);
        addField(quoteText, constraints);
        addLabel("Source", constraints, y++);
        addField(source, constraints);
        addLabel("ISBN", constraints, y++);
        addField(isbn, constraints);
        addLabel("Page", constraints, y++);
        addField(page, constraints);

        for (int i = 0; i < NUMBER_OF_AUTHORS; i++) {
            JPanel authorPanel = layoutAuthor(i, constraints, y);
            y += 2;
        }

        addLabel("Quoter", constraints, y++);
        addFullName(quoterFirstName, quoterLastName, constraints);
        addLabel("URL", constraints, y++);
        addField(quoterUrl, constraints);

        constraints.anchor = GridBagConstraints.EAST;
        constraints.gridx = 0;
        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridy = y++;
        add(addButton, constraints);
    }

    private JPanel layoutAuthor(int i, GridBagConstraints constraints, int y) {
        JPanel authorPanel = new JPanel(new GridBagLayout());

        constraints.gridx = 0;
        constraints.gridwidth = 1;
        constraints.gridy = y;
        authorPanel.add(new JLabel("Author " + (i+1)), constraints);

        constraints.gridwidth = GridBagConstraints.RELATIVE;
        constraints.gridx = 1;
        authorPanel.add(authorFirstNames[i], constraints);

        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridx = 2;
        authorPanel.add(authorLastNames[i], constraints);

        constraints.gridx = 0;
        constraints.gridwidth = 1;
        constraints.gridy = y+1;
        authorPanel.add(new JLabel("URL"), constraints);

        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridx = 1;
        authorPanel.add(authorUrls[i], constraints);

        return authorPanel;
    }
Ouch, that's not much better, and now the author fields are missing in the GUI. I need to add that JPanel... Hack, hack, hack, it's looking OK...
    private void layoutForm() {
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;

        int y = 0;
        addLabel("Quote", constraints, y++);
        addField(quoteText, constraints);
        addLabel("Source", constraints, y++);
        addField(source, constraints);
        addLabel("ISBN", constraints, y++);
        addField(isbn, constraints);
        addLabel("Page", constraints, y++);
        addField(page, constraints);

        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridx = 0;

        for (int i = 0; i < NUMBER_OF_AUTHORS; i++) {
            constraints.gridy = y++;
            JPanel authorPanel = layoutAuthor(i);
            add(authorPanel, constraints);
        }

        addLabel("Quoter", constraints, y++);
        addFullName(quoterFirstName, quoterLastName, constraints);
        addLabel("URL", constraints, y++);
        addField(quoterUrl, constraints);

        constraints.anchor = GridBagConstraints.EAST;
        constraints.gridx = 0;
        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridy = y++;
        add(addButton, constraints);
    }

    private JPanel layoutAuthor(int i) {
        JPanel authorPanel = new JPanel(new GridBagLayout());

        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;
        constraints.gridwidth = 1;
        authorPanel.add(new JLabel("Author " + (i+1)), constraints);

        constraints.gridwidth = GridBagConstraints.RELATIVE;
        constraints.gridx = 1;
        authorPanel.add(authorFirstNames[i], constraints);

        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridx = 2;
        authorPanel.add(authorLastNames[i], constraints);

        constraints.gridx = 0;
        constraints.gridwidth = 1;
        constraints.gridy = 1;
        authorPanel.add(new JLabel("URL"), constraints);

        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridx = 1;
        authorPanel.add(authorUrls[i], constraints);

        return authorPanel;
    }
Now I can extract the body of layoutAuthor into an inner class and inline the layoutAuthor method...
    private void layoutForm() {
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.WEST;

        int y = 0;
        addLabel("Quote", constraints, y++);
        addField(quoteText, constraints);
        addLabel("Source", constraints, y++);
        addField(source, constraints);
        addLabel("ISBN", constraints, y++);
        addField(isbn, constraints);
        addLabel("Page", constraints, y++);
        addField(page, constraints);

        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridx = 0;

        for (int i = 0; i < NUMBER_OF_AUTHORS; i++) {
            constraints.gridy = y++;
            add(new AuthorPanel(i), constraints);
        }

        addLabel("Quoter", constraints, y++);
        addFullName(quoterFirstName, quoterLastName, constraints);
        addLabel("URL", constraints, y++);
        addField(quoterUrl, constraints);

        constraints.anchor = GridBagConstraints.EAST;
        constraints.gridx = 0;
        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridy = y++;
        add(addButton, constraints);
    }

    private class AuthorPanel extends JPanel {
        AuthorPanel(int i) {
            setLayout(new GridBagLayout());

            GridBagConstraints constraints = new GridBagConstraints();
            constraints.anchor = GridBagConstraints.WEST;
            constraints.gridwidth = 1;
            add(new JLabel("Author " + (i+1)), constraints);

            constraints.gridwidth = GridBagConstraints.RELATIVE;
            constraints.gridx = 1;
            add(authorFirstNames[i], constraints);

            constraints.gridwidth = GridBagConstraints.REMAINDER;
            constraints.gridx = 2;
            add(authorLastNames[i], constraints);

            constraints.gridx = 0;
            constraints.gridwidth = 1;
            constraints.gridy = 1;
            add(new JLabel("URL"), constraints);

            constraints.gridwidth = GridBagConstraints.REMAINDER;
            constraints.gridx = 1;
            add(authorUrls[i], constraints);
        }
    }
Great, still looking good, but those three author arrays are bugging me. Let's get rid of those...

I could show you all the things I did to remove those arrays, but this post is getting way too long...and I'm sure it's very boring. It only took a few minutes. I simply replaced the three arrays with an AuthorPanel array.

Hopefully we're a bit closer to testing. I'll have to wait until tomorrow, though. My stop is coming up...here's what the AuthorPanel class looks like...

    private class AuthorPanel extends JPanel {
        private final JTextField firstName = new JTextField(7);
        private final JTextField lastName = new JTextField(7);
        private final JTextField url = new JTextField(15);

        AuthorPanel(int authorNumber) {
            setLayout(new GridBagLayout());

            GridBagConstraints constraints = new GridBagConstraints();
            constraints.anchor = GridBagConstraints.WEST;
            constraints.gridwidth = 1;
            add(new JLabel("Author " + authorNumber), constraints);

            constraints.gridwidth = GridBagConstraints.RELATIVE;
            constraints.gridx = 1;
            add(firstName, constraints);

            constraints.gridwidth = GridBagConstraints.REMAINDER;
            constraints.gridx = 2;
            add(lastName, constraints);

            constraints.gridx = 0;
            constraints.gridwidth = 1;
            constraints.gridy = 1;
            add(new JLabel("URL"), constraints);

            constraints.gridwidth = GridBagConstraints.REMAINDER;
            constraints.gridx = 1;
            add(url, constraints);
        }

        public boolean hasText() {
            return firstName.getText() != "" || lastName.getText() != "" || url.getText() != "";
        }

        public String getFirstName() {
            return firstName.getText();
        }

        public String getLastName() {
            return lastName.getText();
        }

        public String getUrl() {
            return url.getText();
        }
    }

[/projects/quotes] permanent link

Mind your Metaphors (take 3)

Enjoying Laurent's post on Analogies. This is a topic that will need to be continually brought up until people stop using analogies they don't understand. I like what Steve McConnell said:
"By comparing a topic you understand poorly to something similar you understand better, you can come up with insights that result in a better understanding of the less-familiar topic." --Code Complete, 1st edition, p. 7
What I take from this is: Don't compare a topic you understand poorly to something similar that you understand better if you want insight into the topic you understand better.

Mary Poppendeick is the best example I've found of someone who had a deep understanding of a topic (lean manufacturing) and applied it to something new (software development), with great success.

[/links] permanent link


powered by blosxom