|
Red Squirrel Reflections
Dave Hoover explores the psychology of software development
|
|
Tue, 22 Feb 2005Uncle Bob is ranting about the next big thing..."I, for one, hope there isn't a next big thing. Or rather, I hope the next big thing is the big thing that we've needed for the last thirty years and haven't had the guts to actually say. I hope the next big thing is professionalism. Perhaps a better word would be Craftsmanship." Sun, 20 Feb 2005Jason posted a great quote from an interesting article about Low Teck Seng (gotta love that name) and what he's doing with Problem-Based Learning at Republic Polytechnic in Singapore."At the rate that content and knowledge are developed today, I think we must emphasize the process of learning, rather than content delivery."This reminds me of my favorite quote from A Pattern Language: "In a society which emphasizes teaching, children and students -- and adults -- become passive and unable to think or act for themselves. Creative, active individuals can only grow up in a society that emphasizes learning instead of teaching." p. 100 Sat, 19 Feb 2005In the summer of 2002 I read Software Craftsmanship. That book has had a huge influence on my career as a software developer, perhaps even more than Extreme Programming Explained or The Psychology of Computer Programming. While Beck provided me with a vision for how I wanted to do my job and Weinberg showed me that programming has a timeless quality, it was Pete McBreen that fundamentally altered the course of my career. Upon reading his book, my quest to apprentice under a master craftsman began. It has been a rewarding journey.Having no formal training in computer science, or anything close to it, like say, math, physics, or electrical engineering, I was at a loss for how I was ever going to get ahead in the world of programming. The concept of craftsmanship, though, gave me hope. It's simple: software developers are not scientists or engineers, they are craftsmen and craftswomen...um, craftsfolk. Regardless of whether that is true for you, it is true for me. Because dude, I am not, nor will I ever be, a scientist or an engineer. But I could be a craftsman. The fundamental learning situation is one in which a person learns by helping someone who really knows what he is doing. --A Pattern Language, p. 413Since 2002, I have worked hard to put myself into situations where I could help someone who really knew what he was doing. I am thankful that I have been able to accomplish this feat more than once in that time. Like any learning experience, the more you learn, the more you realize how much more you have yet to learn. Working with master craftsfolk will introduce you to your ignorance in short order. But do not despair! This is an opportunity to advance within your craft...if only you can humble yourself to take on the role of the apprentice. [/craftsmanship] permanent link Wed, 16 Feb 2005Last month I blogged about ping-pong programming. That post spawned some online conversations that kicked off a series of unexpected events. One of these events was StickyMinds asking me to re-write the blog post as a column. I was surprised at their level of interest in such a simple, extreme-programming-specific, practice.StickyMinds asked me to provide them with a head shot and a bio. As I was looking at the other columnists for the site, I quicky became aware of my relative lack of experience. My first instict was to get sarcastic... Alas, that's not what I sent StickyMinds, but it is generally an accurate reflection of how I view myself relative to my fellow ThoughtWorkers. Taking yet another step into the surreal, StickyMinds wants me to write N columns for developers who think of their work as a craft. Whoa. When it comes to software craftsmanship, I feel like I'm an apprentice. Maybe on good days, a journeyman. Nevertheless, this is a great opportunity, and I'm going to take advantage of it. Look for more blog posts under the general category of craftsmanship. I'm going to start by focusing on the concepts of beginner's mind, humility, and the search for a master craftsman. If you're a blogger, blog on those topics so I can steal your ideas. Or at least link to them. ;-) [/craftsmanship] permanent link Sun, 13 Feb 2005I'm reading a collection of sermons given by C. S. Lewis during World War II. My favorite thus far has been "Learning in War-Time." I think I must have written down half the content of the sermon as I transcribed my favorite quotes. Here's one:"If we let ourselves, we shall always be waiting for some distraction or other to end before we can really get down to our work. The only people who achieve much are those who want knowledge so badly that they seek it while the conditions are still unfavourable. Favourable conditions never come." p. 60 Fri, 11 Feb 2005Dave Astels has a new blog. He has this to say about unit tests that test the private internals of an object:"This is evidence of a serious misunderstanding of how you should be writing tests: they should be based on behaviour and function.. not state. An object’s state is simply an implementation detail, and should not be exposed to, or relied upon by, other objects. Guess what.. that means no getters & setters, either. But that’s a discussion for another time." Thu, 10 Feb 2005From Bill:"If you want to stifle participation and creativity focus on threats obligations and punishments in your communications with others. Make sure they are much more afraid of doing the wrong thing than they are of eager to do the right thing." Mon, 07 Feb 2005Quotes Manager Project -- Day 13 An ongoing pet project blog...As I explore my solution, I see that subsequent calls to add a new quote actually append the entire document to the previous document. That's obviously not what I want.
Ah, solved the problem I found yesterday.
The
Now for the second problem...
Before the XML output is generated from the I'm feeling stuck, so I'll write a failing test...
public void testShouldWriteDocumentToFileWhenUpdatedMultipleTimes() throws Exception {
Document document = parseStub();
ByteArrayOutputStream output = new ByteArrayOutputStream();
QuoteListener listener = new XmlQuoteListener(document, output);
assertEquals("", output.toString());
listener.update();
listener.update();
String expectedOutput = documentToText(document);
assertEquals(expectedOutput, output.toString());
}
OK, this test fails and identifies the problem.
If I can get it to pass, I'll be in good shape.
Maybe I'll have more clarity on the way home...we're pulling into Chicago...
How about a factory to provide
public void testShouldWriteDocumentToFileWhenUpdatedMultipleTimes() throws Exception {
Document document = parseStub();
ByteArrayOutputStream output = new ByteArrayOutputStream();
Mock factory = mock(OutputFactory.class);
OutputStream ignored = new ByteArrayOutputStream();
factory.expects(atLeastOnce()).method("newOutput")
.will(onConsecutiveCalls(returnValue(ignored), returnValue(output)));
QuoteListener listener = new XmlQuoteListener(document, (OutputFactory) factory.proxy());
assertEquals("", output.toString());
listener.update();
listener.update();
String expectedOutput = documentToText(document);
assertEquals(expectedOutput, output.toString());
}
This won't compile and I'll take a couple biggish steps to make it pass...
public class XmlQuoteListener implements QuoteListener {
private final Document document;
private final OutputFactory factory;
public XmlQuoteListener(Document document, OutputFactory factory) {
this.document = document;
this.factory = factory;
}
public void update() {
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Source source = new DOMSource(document);
Result result = new StreamResult(factory.newOutput());
transformer.transform(source, result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
OK, I've pushed the responsibility down.
I have yet another anonoymous class (this time an OutputFactory) in my
main method...
public static void main(String[] args) throws Exception {
JFrame frame = new JFrame(TITLE);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
final String xmlFile = "play.xml";
Document document = builder.parse(xmlFile);
QuoteListener listener = new XmlQuoteListener(document, new OutputFactory() {
public OutputStream newOutput() throws IOException {
return new FileOutputStream(xmlFile);
}
});
QuoteManager manager = new XmlQuoteManager(document);
manager.addListener(listener);
QuoteManagerPanel quoterManager = new QuoteManagerPanel(manager);
frame.getContentPane().add(quoterManager);
frame.pack();
frame.setVisible(true);
}
I'll run the app and to see if that fixes my problem.
I have an uneasy feeling about creating a new OutputStream every time...
But hey, it worked like a charm!
Time for a check in.
So let's look back at the initial goals and see where we are...
If anyone is interested, I'll post the final source code when I'm finished. [/projects/quotes] permanent link Thu, 03 Feb 2005I came to ThoughtWorks hoping to learn more about software development. And I have. It's been almost a year and I haven't been disappointed. But the most valuable lesson I've learned doesn't have anything to do with software. I have learned what I need in order to do my best work. Crammed right between sleep and spirituality is my connection with my wife and children. When that connection is broken, I'm not doing my best. Probably not even close.I learned this lesson while I was at AYE. I have Alan to thank for my time there. And today I'm thanking Alan again for posting this snippet from The Age: "Australian managers are good at making clear to men that any worker who modifies his schedule to accommodate new fatherhood will be struck off the A-list for the crime of being inadequately 'serious'."Not that I was ever on the A-list, but if putting my family first means I'm not adequately 'serious' about my career, then that's a list I'd rather not appear on. Ironically, I've learned that the most likely way for me to attain that status involves a strong connection with my family. Wed, 02 Feb 2005Laurent is running a dojo. Christophe, one of the members of the dojo, made this comment about why the dojo is different than work:"At work, it's not well seen to voluntarily uncover whole zones of our own ignorance."As a family therapist I was taught to throw off the notion that I had expert knowledge about other peoples' lives. To approach people with a not knowing stance. This is a hard pill to swallow, whether you're a newbie therapist or newbie programmer. Your instincts tell you to hide your ignorance, to feign expert knowledge, but this only stunts your growth and inhibits the work you are trying to accomplish. Taking this lesson with me from one career into another has served me well. I've actually grown attached to feeling ignorant on a daily basis, it lets me know I'm in the right place. "Everybody on an XP team should feel like an idiot regularly." --Extreme Programming Applied: Playing to Win, p. 62, Ken Auer and Roy Miller Quotes Manager Project -- Day 12 An ongoing pet project blog...
I am almost finished with the task of adding a quote to an XML file.
I have the GUI. I have the class that updates the XML
But how does this class interact with
public void testShouldInformListenerOnCallsToNewQuote() throws Exception {
Mock listener = mock(QuoteListener.class);
listener.expects(once()).method("update");
QuoteManager manager = new XmlQuoteManager(parseStub());
manager.addListener((QuoteListener) listener.proxy());
manager.newQuote(new Quote("ingored", new Person("joe", "schmoe")));
}
This leads me to (allow IDEA to) create the QuoteListener interface and
add a new method to the (recently renamed) QuoteManager interface and
XmlQuoteManager. I run the test and jMock tells me that my
QuoteListener interface doesn't have an update method.
I'm feeling confident this morning so I'll take a big step and get to green right away...
public class XmlQuoteManager implements QuoteManager {
private final Document document;
private final Collection<QuoteListener> listeners = new HashSet<QuoteListener>();
public XmlQuoteManager(Document document) {
this.document = document;
}
public void newQuote(Quote quote) {
Node quoteNode = document.createElement("quote");
quoteNode.appendChild( createTextNode(quote) );
if (quote.source != NO_SOURCE)
quoteNode.appendChild( createSourceNode(quote) );
for (Person author : quote.authors)
quoteNode.appendChild( createPersonNode(author, "author") );
if (quote.quoter != NOBODY)
quoteNode.appendChild( createPersonNode(quote.quoter, "quoter") );
document.getDocumentElement().appendChild( quoteNode );
for (QuoteListener listener : listeners)
listener.update();
}
public void addListener(QuoteListener quoteListener) {
listeners.add(quoteListener);
}
...
}
But that won't even compile. I still need to add the update method to the
QuoteListener interface.
F2, then ALT-ENTER, then run tests and we're green.
At this point I could actually code the rest up in the
package com.redsquirrel.quotes.gui;
import com.redsquirrel.quotes.xml.QuoteListener;
import com.redsquirrel.quotes.xml.XmlQuoteManager;
import org.w3c.dom.Document;
import javax.swing.JFrame;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.FileOutputStream;
public class MAIN {
private static final String TITLE = "Quote QuoteManager";
public static void main(String[] args) throws Exception {
JFrame frame = new JFrame(TITLE);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
final String xmlFile = "play.xml";
final Document document = builder.parse(xmlFile);
QuoteManager manager = new XmlQuoteManager(document);
manager.addListener(new QuoteListener() {
public void update() {
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Source source = new DOMSource(document);
Result result = new StreamResult(new FileOutputStream(xmlFile));
transformer.transform(source, result);
} catch (Exception e) {
e.printStackTrace();
}
}
});
QuoteManagerPanel quoterManager = new QuoteManagerPanel(manager);
frame.getContentPane().add(quoterManager);
frame.pack();
frame.setVisible(true);
}
}
I entered a quote and it updated the XML!
But there were a few issues...it added an extra (empty) author and an empty quoter.
Rather than fix it now, with the new code in the main method,
I'll extract it and test. Or more accurately, test then extract.
Starting with an empty test, I pause for a moment to consider what this listener
will need in order to do its work...
Oh, that's easy, just check for which variables had to be declared
package com.redsquirrel.com.quotes.xml;
import org.jmock.MockObjectTestCase;
import org.w3c.dom.Document;
import com.redsquirrel.quotes.xml.QuoteListener;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import java.io.ByteArrayOutputStream;
public class XmlQuoteListenerTest extends MockObjectTestCase {
public void testShouldWriteDocumentToFileWhenUpdated() throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("stub.xml");
ByteArrayOutputStream output = new ByteArrayOutputStream();
QuoteListener listener = new XmlQuoteListener(document, output);
}
}
I'll start by just getting that to compile.
I'm tired and I'm not ready to think of any asserts yet.
Here is the glorious class in it's primordial state:
package com.redsquirrel.com.quotes.xml;
import com.redsquirrel.quotes.xml.QuoteListener;
import org.w3c.dom.Document;
import java.io.OutputStream;
public class XmlQuoteListener implements QuoteListener {
public XmlQuoteListener(Document document, OutputStream output) {
}
public void update() {
}
}
OK, no more laziness, time for an assert (or two)...
public void testShouldWriteDocumentToFileWhenUpdated() throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("stub.xml");
ByteArrayOutputStream output = new ByteArrayOutputStream();
QuoteListener listener = new XmlQuoteListener(document, output);
assertEquals("", output.toString());
listener.update();
String expectedOutput = documentToText(document);
assertEquals(expectedOutput, output.toString());
}
And that fails.
I'll extract the stuff in the main method and see if that does it...
Yep, back to green.
Here it is...
public class XmlQuoteListener implements QuoteListener {
private final Document document;
private final OutputStream output;
public XmlQuoteListener(Document document, OutputStream output) {
this.document = document;
this.output = output;
}
public void update() {
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Source source = new DOMSource(document);
Result result = new StreamResult(output);
transformer.transform(source, result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Hmm, so all my tests are passing, but the problem still persists with the extra XML elements.
I'm going to explore for a while to see where the problem is...
|