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

 

Mon, 24 Jan 2005

Quote Manager Project -- Day 7

An ongoing pet project blog...

Must...write...test...for anonymous ActionListener...

Let's look at our ActionListener:

        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 (authorPanels[i].hasText())
                        authors.add(new Person(authorPanels[i].getFirstName(), authorPanels[i].getLastName(), authorPanels[i].getUrl()));

                Person quoter = new Person(quoterFirstName.getText(), quoterLastName.getText(), quoterUrl.getText());
                new Quote(quoteText.getText(), source, authors, quoter);
            }
        });
I have reduced the number of objects the ActionListener depends on by creating the AuthorPanel. Looks like I could do something similar with the quoter fields. Here we go again...how'd I start last time? Oh yeah, 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);

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

        for (int i = 0; i < NUMBER_OF_AUTHORS; i++) {
            constraints.gridy = y++;
            add(authorPanels[i], constraints);
        }

        layoutQuoter(constraints, y);
       y += 2;

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

    private void layoutQuoter(GridBagConstraints constraints, int y) {
        addLabel("Quoter", constraints, y);
        addFullName(quoterFirstName, quoterLastName, constraints);
        addLabel("URL", constraints, y+1);
        addField(quoterUrl, constraints);
    }

And then an inline method time four, and return a JPanel...
    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(authorPanels[i], constraints);
        }

        constraints.gridy = y++;
       add(layoutQuoter(), constraints);

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

    private JPanel layoutQuoter() {
        JPanel quoterPanel = new JPanel(new GridBagLayout());

        GridBagConstraints constraints = new GridBagConstraints();

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

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

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

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

        constraints.gridwidth = GridBagConstraints.REMAINDER;
        constraints.gridx = 1;
        quoterPanel.add(quoterUrl, constraints);

        return quoterPanel;
    }
This looks very familiar, which is good, because it means I can probably share code with the AuthorPanel. But not yet...first the inner class...

QuoterPanel is almost an exact replica of the AuthorPanel. Let's see if I can extract the duplication... Yep, no problem. I created a concrete PersonPanel which was an exact copy of AuthorPanel except that the constructor takes the entire String for the label, rather than the authorNumber. That way when I need a PersonPanel to represent a quoter, I just pass in "Quoter". Pretty simple.

Now my anonymous ActionListener depends on six objects. I can get that down to four if I group the source fields into their own panel. I think I can do this quickly...I've gone through the steps twice already. I'll take some bigger steps and not bore you with the details...

I have a SourcePanel. There is a lot of GridBagLayout duplication between the three different JPanel derivatives, but I'm not going to worry about that right now. It's time to test...

package com.redsquirrel.com.quotes.gui;

import org.jmock.MockObjectTestCase;
import org.jmock.Mock;

public class AddButtonActionListenerTest extends MockObjectTestCase {
    public void testShouldProvideNewQuoteToManager() {
        Mock manager = mock(Manager.class);
        AddButtonActionListener actionListener = new AddButtonActionListener((Manager) manager.proxy());
        actionListener.actionPerformed(null);
    }
}
This test doesn't even come close to compiling, so I'll let IDEA do the work for me with a series of wonderous F2/ALT-ENTER steps. This leaves me with an empty Manager interface and a stubbed out AddButtonActionListener. I'll run the test.

It passes. Doh! I didn't add any expectations! Here's a simple one...

    public void testShouldProvideNewQuoteToManager() {
        Mock manager = mock(Manager.class);
        manager.expects(once()).method("newQuote").with(isA(Quote.class));

        AddButtonActionListener actionListener = new AddButtonActionListener((Manager) manager.proxy());
        actionListener.actionPerformed(null);
    }
That fails. Good. Ahhh, gotta love that red bar. Gives you a nice sense of purpose: get it to green ASAP. Back to green with this ugly sin.
public class AddButtonActionListener implements ActionListener {
    private final Manager manager;

    public AddButtonActionListener(Manager manager) {
        this.manager = manager;
    }

    public void actionPerformed(ActionEvent e) {
        manager.newQuote(new Quote("foo", new Source("foo", "foo", "foo"), new HashSet(), new Person("foo", "foo", "foo")));
    }
}
I'll make that test a bit more rigorous...
    public void testShouldProvideNewQuoteToManager() {
        Collection<Person> authors = new HashSet<Person>();
       authors.add(new Person("Dee", "Hock", "http://chaordic.org/"));

       String text = "Community is not about profit, it is about benefit.  We confuse them at our peril.";
       Source source = new Source("Birth of the Chaordic Age", "43", "1576750744");
       Quote expected = new Quote(text, source, authors, null);

        Mock manager = mock(Manager.class);
        manager.expects(once()).method("newQuote").with(eq(expected));

        AddButtonActionListener actionListener = new AddButtonActionListener((Manager) manager.proxy());
        actionListener.actionPerformed(null);
    }

That fails, and I'll leave it failing until tomorrow. Here's my stop...

[/projects/quotes] permanent link


powered by blosxom