Hugh Winkler holding forth on computing and the Web

Wednesday, February 08, 2006

Patterns: Another Way To Say "Bloat"

A quote I'd missed at the very end of this oft-linked Paul Graham essay:
...in the OO world you hear a good deal about "patterns". I wonder if these patterns are not sometimes evidence of case (c), the human compiler, at work. When I see patterns in my programs, I consider it a sign of trouble.


It's elementary information theory, sir. Any pattern, or predictable part, of the signal is non-informative bit bloat. Compression programs work by identifying predictable patterns and encoding them simply: if the pattern recurs, you don't have to repeat it verbatim. You can just say, e.g. "Pattern 3 again" to substitute for the longer sequence.

It's the same with programming. Coding patterns is cut and paste. Eclipse will even help you do it, in Java; it has all sorts of nifty shortcut keys to do patterns like "return an array from a collection instance." That's reverse compression: You type Ctrl-Shift-J or whatever, and Eclipse expands it into the pattern for you.

Here's a pattern that gripes me in java: for data access, I usually make two methods for each thing I want to access. Both methods accept all the parameters you need to select the thing; but one method accepts a JDBC connection parameter, while the other constructs and destroys the connection and calls into the first method. The second method is a convenience procedure, while the first one allows you to make the call as part of a series of actions withing the same transaction. You know...


public static long getMetainfoId(UID uid, String objectType){
try {
// notice the pattern within a pattern below (service locator)
Connection conn = ServiceLocator.getInstance().getDataSource().getConnection();

try {
return getMetainfoId(conn, uid, objectType);
} finally{
conn.close();
}

} catch (Exception e) {
throw new RuntimeException(e);
} finally {

}

}

public static long getMetainfoId(Connection conn, UID uid, String objectType){
.... do the real work
}


I have cut and paste that snippet about five hundred times.

I want a way to avoid both compile time and run time bloat. I don't just want a magic macro that saves source code; I want the actual wrapper code to be generic. I asked my friend and colleague Brad to sketch a Lisp solution:

the wrapper function would be something like this:

(defun wrapper (resource-creator resource-usinge-function &rest args)
"resource creator is a function that creates the resource we will
temporarily use. We can also parameterize it if necessary at compile
or run time using lambdas depending upon needs"
(let ((resource (funcall resource-creator)))
(unwind-protect
(apply resource-using-function resource args)
(resource-close resource)))) ; resource close could be a generic
function or parameterized as well


when defining a wrappapble function:

(define-wrapped get-uid (connection metainfold)
(create-connection p1 p1) ; this line specifies the resource creation function
(your code here)) ; the body that does the work and gets wrapped


I'm going to learn Lisp!

1 comment:

James said...

Hey Hugh,

One thing to keep in mind is that, with everything, there are a number of ways to tackle this. While I agree that some languages have a more elegant solution to your issue, another way to approach this in Java is to use the Spring Framework. While not the best framework out there, IMO, it does address what you want. Take a look at their docs on Transaction Management, specifically section 8.3.2.

Although people often put together Hibernate and Spring, if you want direct JDBC access, you can still use their tx manager and get what you need with less code. And, having "AOP" through interceptors around your services is mighty handy as well.

Though, if given the choice, I'd rather utilize ActiveRecord as part of Ruby on Rails. Yeah, you heard me, Ruby (big shocker to Brad, I'm sure).