If you gaze upon the right side of this blog, you will now see a link to my list of recently read books.
This was a pretty interesting story to implement. XP encourages you to always do the simplest thing that will cause all your tests to pass; one of their key phrases in support of this is You Aren’t Going to Need It, abbreviated as YAGNI. The feeling is that, as long as you’re conscientious about refactoring constantly, you’ll be able to maintain a clean, extensible design at all times even without long-term planning.
One example of this is that, when I added the “Read” field, I represented the date as a string, since that was the simplest thing that could possibly work. I didn’t seriously think that YAGNI applied in that case – I was pretty sure I was going to want to eventually generate a list of recently read books, at which point I’d need to be able to sort by date – but I didn’t need it for that particular story.
But now I’ve had to write my Date class (I didn’t see any built-in classes that provided quite the functionality I needed). And doing it now instead of earlier has clearly turned out to be the right choice – while I haven’t saved any time overall (if I’d guessed at the Date interface back then, I probably would have gotten it right), it meant that I got to add the “Read” field faster than I would otherwise have been able to, which is good.
The story did, however, point out a more insteresting refactoring that it’s now time to do. When I started generating HTML pages, I thought about writing some sort of HtmlWriter class, to handle things like opening and closing elements, adding attributes to elements, indentation, etc. But I decided that it was overkill to do so at the time: all the HTML pages were for what I’m calling ‘entities’ (items in the database, such as authors, books, or series), so the structure all looked the same. And if I stuck the HTML generating code in my Entity class, I could use virtual functions to good effect to generate the HTML in a straightforward matter.
Now, however, I’m generating an HTML page that doesn’t correspond to an entity, namely the list of recently read books. Which results in a bit of code duplication; it’s not too serious yet, but it bothers me a little bit. I’m honestly not sure whether XP would have me pull out a full-fledged HtmlWriter now: the only other non-entity pages on the radar screen are other indices of various sorts (lists of authors sorted by name, books sorted by title, etc.), so maybe the simplest design would be to have an Index base class that knows how to generate HTML for indices, just like Entity knows how to generate HTML for entities, and leave it at that. But I think the current Entity class already has a mixture of responsibilities (both storing entity-related data and writing out HTML); having two classes like that, with one of their responsibilities shared, wouldn’t leave a good taste in my mouth. And XP likes your classes to have a single responsibility, and to clearly express that responsibility. So I think now is the time to pull out an HtmlWriter class.
Here, unlike the date example, YAGNI does apply: the HtmlWriter that I’m going to pull out is going to be the simplest such class to let me generate the pages in question, and won’t have all of the features that I’d earlier envisioned. And having two concrete, distinct places where I’m going to use it will help clarify its class interface enormously.
Also, having this HtmlWriter class will have other benefits. For example, I’d been thinking that it would be a good idea for my internal links to be absolute instead of relative; I’ll have to do that eventually if I decide to provide multiple ways to access single page from different locations within a directory hierarchy. (E.g. the Walter Benjamin page might be accessible both via dbcdb/1 and via dbcdb/author/walter-benjamin.) And implementing that will take five minutes once I’ve refactored out HtmlWriter: I’ll add a link prefix to the constructor and tell its openLink method (or whatever it will be called) to stick that prefix at the start of the href. (And at the start of the stylesheet link, too.) Simple.
A couple more potential refactorings made themselves known during this story, too. I have a Collection class that keep tracks of entities; it also currently knows how to write out the HTML files corresponding to the entites in the database. Which was fine when doing so was a simple loop over all entities, constructing a file name for each one and telling it to write itself to that file.
But now there’s an extra step, generating the page of recently read books. At this point, I have two methods that are more presentational, and they could both be written using Collection’s public interface. So it’s time to split out that functionality to a different class, CollectionWriter. And that class will be even more useful in the future, both when I add more indices and when I change the database storage method to use a SQL database instead of a Java file. (The latter change will probably require further refactorings, but never mind that.) I’ll probably do that refactoring now – after all, CollectionWriter will need to know about HtmlWriter while Collection won’t, so the split goes with my HtmlWriter refactoring. (Plus, it will take something like two minutes – I just have to move a handful methods to a new class and their tests to a new test class.)
And, having written the code to generate the a sorted list of recently read books (which took more steps than I expected to get right), I can see how that code will get generalized to handle other fields. Basically, I want to go through all entities of a certain type (Book, Author, whatever), and sort them by some field on that type (date, title, name). And I should be able to write some generic code to do that. Which I will do. But some other day; that can wait until I generate another index, at which point I’ll have two examples to work from and refactor out the common code.
It’s a joy to work like this; on the one hand (men), the code is clean enough to be easy and pleasant to work with, while on the other hand (de), it’s pregnant with possibilities, with refactorings lurking under the surface that I know I can carry out if I need to. I think Michael Feathers said something along the lines that you shouldn’t try to have your code always express absolutely everything it could, you should just try to make sure that it’s one refactoring away from expressing anything latent in the code. Which is how I feel about this code, and is a good rule of thumb: YAGNI suggests that too much refactoring might be a waste, but if a story appears for which the refactoring is important, you want to be able to carry it out ASAP.
And now I have an excuse to delve into web design again: there are enough dbcdb-related links that I think I could begin to populate a sidebar on my generated pages. Which I would have to style appropriately. Fun!
There are no revisions for this post.