in reply to Re: Idea on a Base class API
in thread Idea on a Base class API

I agree that there are a number of ways by which the definition of individual accessors can be defined. However, I'd like to give a snippet to demonstrate the english-ness of this method.

my @attribute_names = ('attr1', 'attr2', 'attr3'); foreach my $attribute (@attribute_names) { my $value = $self->get($attribute); print "$attribute => $value\n"; }

This is as opposed to how it would have to be using get_foo() or set_foo(). That would look something like:

my @attribute_names = ('attr1', 'attr2', 'attr3'); foreach my $attribute (@attribute_names) { my $function_name = "get_$attribute"; my $value = $self->$function_name; print "$attribute => $value\n"; }

I ask you ... which is clearer? Of course, there's an even clearer way to do that exact same thing:

my @attr_names = ('attr1', 'attr2', 'attr3'); my @values = $self->get(@attr_names); print "$attr_names[$_] => $values[$_]\n" for (0 .. $#attr_names);

Personally, I think that Example 3 is the cleanest of all.

Now, the biggest problem I have with Example 2 (and its cousin, $self->$attribute;) is that you're using a string as a function name. Can you guarantee that this function exists? Maybe, but it's a lot of work, and you restrict your options. For code that will always handle exceptions gracefully, you want to have your function names hard-coded. That way, you will always be executing something.

A nifty benefit, too, is that you're expanding your options of viable code, as you can see from Example 3.

The expected behavior of define_attributes() when called on a class with instances out there already is ... well ... not determined yet. Certain implementations of objects mandate that the attributes will get added immediately, and others mandate that it has to be done by hand. I, personally, would say that if you call define_attributes again, that you will add new attributes to all instances, present and future, and that's just the way it has to be.

A more important problem is what happens when you call define_attributes() on the same attribute. Or, what happens when Foo overrides the allowable types on attrib1 inherited from Bar. These are the decisions that have to be put into the contract, and that's why I'm putting the draft of the contract out there for everyone to read and comment on. :)

Now, to your objection about needing to add extra conditionals when dealing with set() ... that's a good objection, and one that I hadn't really thought of. Three possible answers immediately come to mind:

  1. Do something like $self->set(foo => $val) unless $foo % PI; ... put the conditional outside the call to set().
  2. Add something to the allowed type, something like MULTIPLE(x) where x is something you've supplied. So, you could have:
    FooClass->define_attributes( foo => { Type => 'MULTIPLE(x)', Default => 0 }, );
    And, no, I'm not quite sure how that would be implemented. But, that's not the point in this discussion. The idea is to figure out what is desired.
  3. Create a set_foo() method that will encapsulate Option 1.
Do I know which is the best answer? Not yet. If I were to implement something right now, I'd use Option 3. (In fact, I have used Option 3, in a slightly different context.)