|
From: Ken A. <kan...@bb...> - 2004-02-02 22:10:58
|
I tried sending an earlier version of this message, but i got hints that it is in
mail limbo somewhere.
Constructing an object can be verbose in Java, because often you need
a temporary variable followed by a raft of setter calls. The raft
grows even larger if some fields have values that must be constructed
the same way. Here's an example from the JabberBeans API (though you
can probably find bigger examples in uses of Swing):
IQAuthBuilder iqa = new IQAuthBuilder();
iqa.setUser(user);
iqa.setPassword(password);
iqa.setResource(resource);
InfoQueryBuilder iqb = new InfoQueryBuilder();
iqb.setType("set");
iqb.setIdentifier("set-1");
iqb.addExtension(iqa);
Some languages, like Python and Common Lisp, lets you construct an
object and initialize it with keyword value pairs. Tim's JLIB even
does this one better by bypassing the keyword altogether and just
allowing extra arguments to be assigned to the "most appropriate"
field. For example you can make a red Quit button with
(Button "Quit" Color.red$) or (Button Color.red$ "Quit").
Unfortunately this requires you to come up with a different API than
the underlying Java, and there may not be a good choice of "most
appropriate" field, if for example, all the fields are Strings.
Here's a procedure (with object .method val ... .method val ...) that
generalizes keyword value pairs. The keywords are Javadot methods
and they can take more than on value:
(define (with what . kvs)
(define (method? x) (instanceof x JavaMethod.class))
(define (op kvs)
(if (null? kvs) what
(if (method? (car kvs))
(args (car kvs) '() (cdr kvs))
(error {expected operator, but got [(car kvs)]}))))
(define (args method sofar kvs)
(if (null? kvs)
(begin (apply method what (reverse sofar))
what)
(if (method? (car kvs))
(begin (apply method what (reverse sofar))
(op kvs))
(args method (cons (car kvs) sofar) (cdr kvs)))))
(op kvs))
Here's what the above Java code might look like in JScheme:
(with (InfoQueryBuilder.)
.setType "set" .setIdentifier "set-1
.addExtension (.build (with (IQAuthBuilder.) .setUsername user
.setPassword password
.setResource resource))))))
Here's another example constructing an FTP client using an API from
http://www.enterprisedt.com/downloads.html:
(define (ftpClient user password remoteMachine directory)
(define ftpc (with (FTPClient. remoteMachine)
.debugResponses #t .login user password))
(if (not (or (not directory) (isNull directory) (equal? directory "")))
(.chdir ftpc directory))
ftpc)
The .login method takes two arguments. The nice thing about this
idiom is that objects can be initialized where they're needed and
remain anonymous in many cases.
We could gain some performance by writing this as a macro, but this
hasn't been an issue so far.
k
|