Beautiful Code [126]
public void print(PrintWriter out) {
out.print(leader);
out.print(tag);
if (parts != null) {
parts.print(out);
} else {
out.print(body);
}
out.print(end);
if (more != null) {
more.print(out);
} else {
out.print(trailer);
}
}
In my opinion, Java code doesn't get much more elegant than that.
For a long while, I've subscribed to the rule of thumb that constructors should not do the bulk of work in a class. All they should do is put an object into a valid state and leave the real work to the other methods. In general, people don't expect object creation to be an expensive operation, and they are often surprised when it is. However, the construction code for Parse is undeniably elegant. There's a beautiful symmetry to the way it breaks an HTML string down into a couple hundred Parse objects and then reconstitutes it using a single method: print.
If the parsing code and the representation code were in separate classes, the framework might be able to handle different formats, such as XML or RTF; however, restricting the design to handling HTML feels like the right choice. It keeps the framework small and easy to understand—the parsing code is only about 25 lines. By fixing a single choice, FIT became a simpler, more robust framework. One of the deepest lessons in design is that you can gain a great deal if you hold the right things constant.
Another interesting thing about Parse objects is that they don't use collections to hold references to their neighbors. They use parts and more as direct links. This makes the code a little Lisp-y, but if you are used to looking at things from a functional point of view, it's very straightforward.
Here's an example of this style of coding. The last method in Parse returns the last element in the more sequence of a Parse:
public Parse last() {
return more==null ? this : more.last();
}
Again, it's interesting that all of these fields on Parse are public. The framework can't anticipate every way that users might need to modify them. Yes, there are convenience methods such as addToTag and addToBody, but anyone who wants to can directly modify the fields that they act on. FIT gives you that power at its own expense: future versions of FIT can't easily revoke that access. It's not the sort of choice that all framework designers can or should make, but if you can live with the consequences, it's a valid choice.
Framework for Integrated Test: Beauty Through Fragility > Conclusion
6.5. Conclusion
Many of us in the industry have learned our lessons through the school of hard knocks. We've run into situations where software we've written earlier is not as extensible as we wish. Over time, we've reacted by gathering rules of thumb that attempt to preserve extensibility by restricting choices. If you are developing a framework and you have thousands of users, this may be the best thing that you can do, but it isn't the only way.
The alternative that FIT demonstrates is radical: try to make the framework as flexible and concise as you can, not by factoring it into dozens of classes but by being very careful about how each class is factored internally. You make methods public so that when users want to stray from the normal course, they can, but you also make some hard choices, like FIT's choice of HTML as a medium.
If you design a framework like this, you end with a very different piece of software. Once the classes are opened up, it will be hard to change them in future releases, but what you produce may be small and understandable enough to lead people to create something new.
And that isn't a trivial feat. The world is filled with frameworks that are a little too hard to understand and look like they have a bit too much investment in them—so much investment that it's hard to justify reinventing