On 7/16/04 6:38 PM, David Wheeler wrote:
> I'm always open to suggestions, esp. since I'm still trying to decide
> how to solve this problem in my own applications.

Well, I think I've already given all of my advice, but I'll try to wrap it
all up in one post.

First, IME, the vast majority of my "get" methods don't allow any arguments.
I often can't even think of a reason why they would.  If I want to return
something different, it's usually done by deriving another value from what
is returned by the original accessor.

For example, imagine a names() method that gets a list of names.  If I only
want the names that are "special" in some way, I'm not inclined to add a
"special => 1" argument to the name() accessor.  Instead, I'd add a new
method named special_names().  Or if I want to be more arbitrary:
names_filtered(filter => ...) or whatever.

In the rare case that I use arguments in an accessor, I still use the same
method name to get and set.  I distinguish the cases with the simple rule
mentioned earlier.  If there is one argument, set.  Otherwise, get (possibly
with modifications as indicated by the arguments, if any).

If I ever needed to "set" with multiple arguments in such a method, I'd just
require them to be packed up into a single ref arg.  And even that isn't
always necessary if you know something about the range of valid values for
the "set" case.

Here's a real-world example. I have a method-maker variant that makes "date"
get/set methods.  If passed a single argument, it is validated parsed by my
simple date parser (which I would love to replace by DateTime->parse(...) ;)
and stored internally as a DateTime object.  If passed no args, it returns
the DateTime object.

It also takes a handful of named args in the "get" case.  Sometimes I want
the date formatted in a "non-default" way.  (Actually, this was before
DateTime objects stringified at all, IIRC, but regardless...)  In that case,
I do something like this:

    $obj->start_date(format => '%B %d %Y at %T')

The format arg just gets passed to DateTime's strftime() method, of course.
Similarly, if I wanted a truncated clone to be returned, I call:

    $obj->start_date(truncate => 'day')

You get the idea.

Why not make separate methods as advised earlier?   Well, mostly because
every one of my DB-backed objects has at least two dates (create/mod) and
usually more.  I've already got one get/set method for every column in a DB
record.  If I had to make separate methods for every potential variant of
every date method, the number of methods would explode.  (Separate set/get
methods would double the number as well.)

It's also convenient to be able to pass format and truncate args to any date
method without having to accurately predict beforehand which dates I'm
likely to want to format or truncate, and without making separate truncate
and format methods for all of them "just in case."

Finally, why not simply use functions to modify the return values like this?

    format_date($obj->start_date, '%B %d %Y at %T')

    truncate_date($obj->start_date)

I guess it comes down to a style choice.  I prefer the more o-o style to the
functional style.  I Also don't like having to import symbols all over, and
certainly don't want to fully-qualify everything.

In reality, functions are doing the work behind the scenes anyway (my own
parse_date() and format_date() library functions), but I only have to import
them in one place: the method-maker class (and actually I fully-qualify them
there because I'm a miser ;)

As for this:

    $obj->start_date(format => '%B %d %Y at %T')

being nothing more than syntactic sugar for this:

    $obj->start_date->strftime('%B %d %Y at %T')

the first one gives me a bit more flexibility if, say, I decide to add new
format specifiers.  As it turns out, that's just what I did, and I didn't
end up with a mix of "old style" $obj->start_date->strftime(...) calls and
"new style" $obj->start_date(format => ...) calls when I did it.

Similar arguments apply to the preference of:

    $obj->start_date(truncate => 'day')

over this:

    $obj->start_date->clone->truncate(to => 'day')

---

But honestly, 99.9% of my get/set methods don't take any args in the "get"
case.  I really, really like the cleanliness of using the same method name
for both.  The only thing that bothers me at all is the runtime overhead of
arg checking, but as I mentioned before, even that will go away when
efficient multimethod dispatch arrives in Perl 6.

In the "date" get/set case in Perl 6, I'd have 3 methods with the same name
but three different signatures: "no args", "named args", and "a single
string arg")  Sorry if I can't recall the Perl 6 signature syntax off the
top of my head... ;)

-John


Reply via email to