|
Red Squirrel Reflections
Dave Hoover explores the psychology of software development
|
|
Sat, 29 Jan 2005Quotes Manager Project -- Day 11 An ongoing pet project blog...
I'm back to green.
I needed a
public void newQuote(Quote quote) {
Node root = document.getDocumentElement();
Node quoteNode = document.createElement("quote");
Node textNode = document.createElement("text");
Node textSection = document.createCDATASection(quote.text);
textNode.appendChild(textSection);
quoteNode.appendChild(textNode);
Node sourceNode = document.createElement("source");
Attr sourceIsbn = document.createAttribute("isbn");
sourceIsbn.setValue(quote.source.isbn);
sourceNode.getAttributes().setNamedItem(sourceIsbn);
sourceNode.setTextContent(quote.source.title);
quoteNode.appendChild(sourceNode);
for (Person author : quote.authors) {
Attr authorUrl = document.createAttribute("url");
authorUrl.setValue(author.url);
Node authorNode = document.createElement("author");
authorNode.getAttributes().setNamedItem(authorUrl);
Node authorFirst = document.createElement("first");
authorFirst.setTextContent(author.firstName);
authorNode.appendChild(authorFirst);
Node authorLast = document.createElement("last");
authorLast.setTextContent(author.lastName);
authorNode.appendChild(authorLast);
quoteNode.appendChild(authorNode);
}
root.appendChild(quoteNode);
}
And here's the latest test...
public void testShouldWriteXmlWhenGivenNewQuoteWithPageNumber() throws Exception {
String text = "The major problems of our work are not so much <i>technological</i> as <i>sociological</i> in nature.";
String isbn = "0932633439";
String title = "Peopleware: Productive Projects and Teams";
String page = "4";
String firstName1 = "Tom";
String lastName1 = "DeMarco";
String url1 = "http://www.systemsguild.com/GuildSite/TDM/Tom_DeMarco.html";
String firstName2 = "Timothy";
String lastName2 = "Lister";
String url2 = "http://www.cutter.com/consultants/tlbio.html";
Collection<Person> authors = new ArrayList<Person>(2);
authors.add(new Person(firstName1, lastName1, url1));
authors.add(new Person(firstName2, lastName2, url2));
Quote quote = new Quote(text, new Source(title, page, isbn), authors, null);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("stub.xml");
assertEquals(0, document.getElementsByTagName("quote").getLength());
Manager manager = new XmlQuoteManager(document);
manager.newQuote(quote);
assertEquals(1, document.getElementsByTagName("quote").getLength());
String textContent = documentToText(document);
assertTrue(-1 != textContent.indexOf("<text><![CDATA[" + text + "]]></text>"));
// assertTrue(-1 != textContent.indexOf("<source isbn=\""+ isbn + "\" page=\"" + page + "\">" + title + "</source>"));
// assertTrue(-1 != textContent.indexOf("<author url=\""+ url1 + "\"><first>" + firstName1 + "</first><last>" + lastName1 + "</last></author>"));
// assertTrue(-1 != textContent.indexOf("<author url=\""+ url2 + "\"><first>" + firstName2 + "</first><last>" + lastName2 + "</last></author>"));
}
I'll uncomment those tests and see where I am.
Arg, they're failing on the next line. Something to do with the source, it's missing a page.
Oh, there's a good reason for that, I never coded it. This is why I write tests first.
So I added the page attribute, but then my other test broke because it doesn't have a page.
Time for a handy-dandy
if (quote.source.page != null && !quote.source.page.trim().equals("")) {
Attr sourcePage = document.createAttribute("page");
sourcePage.setValue(quote.source.page);
sourceNode.getAttributes().setNamedItem(sourcePage);
}
I need a test with a quoter.
public void testShouldWriteXmlWhenGivenNewQuoteWithQuoter() throws Exception {
String text = "To be able to understand when the rules don't apply, you need to completely understand when they do.";
String isbn = "0201760436";
String title = "Agile Software Development Ecosystems";
String page = "31";
String quoterFirstName = "Bob";
String quoterLastName = "Charette";
String quoterUrl = "http://www.cutter.com/consultants/charetter.html";
Person quoter = new Person(quoterFirstName, quoterLastName, quoterUrl);
String firstName = "Jim";
String lastName = "Highsmith";
String url = "http://www.adaptivesd.com/";
Collection<Person> authors = new ArrayList<Person>(1);
authors.add(new Person(firstName, lastName, url));
Quote quote = new Quote(text, new Source(title, page, isbn), authors, quoter);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("stub.xml");
assertEquals(0, document.getElementsByTagName("quote").getLength());
Manager manager = new XmlQuoteManager(document);
manager.newQuote(quote);
assertEquals(1, document.getElementsByTagName("quote").getLength());
String textContent = documentToText(document);
assertTrue(-1 != textContent.indexOf("<text><![CDATA[" + text + "]]></text>"));
assertTrue(-1 != textContent.indexOf("<source isbn=\""+ isbn + "\" page=\"" + page + "\">" + title + "</source>"));
assertTrue(-1 != textContent.indexOf("<author url=\""+ url + "\"><first>" + firstName + "</first><last>" + lastName + "</last></author>"));
assertTrue(-1 != textContent.indexOf("<quoter url=\""+ quoterUrl + "\"><first>" + quoterFirstName + "</first><last>" + quoterLastName + "</last></quoter>"));
}
This fails where I expected, on the last assert.
I'll put in the quoter code on the way home. We just pulled into Chicago...
Back to green. I basically just copied and pasted the body of the author loop and after a couple SHIFT-F6's the variables had the appropriate names.
public void newQuote(Quote quote) {
Node root = document.getDocumentElement();
Node quoteNode = document.createElement("quote");
Node textNode = document.createElement("text");
Node textSection = document.createCDATASection(quote.text);
textNode.appendChild(textSection);
quoteNode.appendChild(textNode);
Node sourceNode = document.createElement("source");
Attr sourceIsbn = document.createAttribute("isbn");
sourceIsbn.setValue(quote.source.isbn);
sourceNode.getAttributes().setNamedItem(sourceIsbn);
if (quote.source.page != null && !quote.source.page.trim().equals("")) {
Attr sourcePage = document.createAttribute("page");
sourcePage.setValue(quote.source.page);
sourceNode.getAttributes().setNamedItem(sourcePage);
}
sourceNode.setTextContent(quote.source.title);
quoteNode.appendChild(sourceNode);
for (Person author : quote.authors) {
Attr authorUrl = document.createAttribute("url");
authorUrl.setValue(author.url);
Node authorNode = document.createElement("author");
authorNode.getAttributes().setNamedItem(authorUrl);
Node authorFirst = document.createElement("first");
authorFirst.setTextContent(author.firstName);
authorNode.appendChild(authorFirst);
Node authorLast = document.createElement("last");
authorLast.setTextContent(author.lastName);
authorNode.appendChild(authorLast);
quoteNode.appendChild(authorNode);
}
if (quote.quoter != null) {
Attr quoterUrl = document.createAttribute("url");
quoterUrl.setValue(quote.quoter.url);
Node quoterNode = document.createElement("quoter");
quoterNode.getAttributes().setNamedItem(quoterUrl);
Node quoterFirst = document.createElement("first");
quoterFirst.setTextContent(quote.quoter.firstName);
quoterNode.appendChild(quoterFirst);
Node quoterLast = document.createElement("last");
quoterLast.setTextContent(quote.quoter.lastName);
quoterNode.appendChild(quoterLast);
quoteNode.appendChild(quoterNode);
}
root.appendChild(quoteNode);
}
There's nothing that drives me crazier than developers who copy and paste code
without refactoring it out. So it's time to refactor this monsterous method.
After a series of CTRL-ALT-M's and CTRL-ALT-N's,
otherwise known as extract method and inline variable, the method looks a lot nicer.
public void newQuote(Quote quote) {
Node quoteNode = document.createElement("quote");
quoteNode.appendChild( createTextNode(quote) );
quoteNode.appendChild( createSourceNode(quote) );
for (Person author : quote.authors)
quoteNode.appendChild( createAuthorNode(author) );
if (quote.quoter != null)
quoteNode.appendChild( createQuoterNode(quote) );
document.getDocumentElement().appendChild( quoteNode );
}
The tests are still green. There is duplication between the
createQuoterNode and createAuthorNode.
Let's get rid of that ASAP.
I'll start by seeing what happens if I use the createAuthorNode rather than
the createQuoterNode.
public void newQuote(Quote quote) {
Node quoteNode = document.createElement("quote");
quoteNode.appendChild( createTextNode(quote) );
quoteNode.appendChild( createSourceNode(quote) );
for (Person author : quote.authors)
quoteNode.appendChild( createAuthorNode(author) );
if (quote.quoter != null)
quoteNode.appendChild( createAuthorNode(quote.quoter) );
document.getDocumentElement().appendChild( quoteNode );
}
That breaks the quoter test because now the quoter is showing up as an author.
I introduce parameter (CTRL-ALT-P) on tag name, make sure the quoter code is
passing in "quoter" and rename method (SHIFT-F6).
Voila! Back to green, and IDEA tells me I have dead code (createQuoterNode)
I can delete. So I do (I really love that feeling).
Here's the current state of the method:
public void newQuote(Quote quote) {
Node quoteNode = document.createElement("quote");
quoteNode.appendChild( createTextNode(quote) );
quoteNode.appendChild( createSourceNode(quote) );
for (Person author : quote.authors)
quoteNode.appendChild( createPersonNode(author, "author") );
if (quote.quoter != null)
quoteNode.appendChild( createPersonNode(quote.quoter, "quoter") );
document.getDocumentElement().appendChild( quoteNode );
}
I should find some other permutations to test before moving on.
Like authors without URLs or sources without ISBNs.
But I won't bore you with them now.
|