in reply to Method Chaining and Accessors

TheDamian suggests the use of get_foo and set_foo.This is because you have some attributes for which there will be a get_foo but no set_foo so the example was that a foo sub that is both a getter and setter has no way to indicate that you can not actually set foo, because if it dies then you have to wrap all your get/setters in eval { ... } and nobody wants that.

package Foo; use Readonly my $SPAM_FACTOR = 22; use Readonly my $SPAM_FUDGE = 42; # seasonally adjusted # cause-cause-bundles-of-problems constructor sub new { bless {spam=>4} , shift } # you can get or set bar with $that->bar sub bar { my ($this, $newbar) = @_; if (@_) $this->{bar} = $newbar; return $this->{bar}; } # you can't set baz, because it's derrived automatically from other at +tribute sub baz { my ($this) = @_; # just ignore @_ ... perhaps warn "you cant change baz" return $this->{spam} * $SPAM_FACTOR + $SPAM_FUDGE; } # some time later pacakge main; my $foo = Foo->new(); $foo->bar( 'moose' ); # neat, bar is moose $foo->baz( 'shoe' ); # not so much, worst of all, it looks right! printf "I have a lovely foo, it has %s bar and %s baz " , $foo->bar, $ +foo->baz; # hey! where's my shoe!?

if you use the get_bar and a set_bar convention, perl will complain bitterly when you call set_baz when the pacakge Foo does not provide one and you'll know straight away that you can't set_baz after all.

@_=qw; ask f00li5h to appear and remain for a moment of pretend better than a lifetime;;s;;@_[map hex,split'',B204316D8C2A4516DE];;y/05/os/&print;

Replies are listed 'Best First'.
Re^2: Method Chaining and Accessors
by doom (Deacon) on Apr 05, 2007 at 23:48 UTC

    TheDamian suggests the use of get_foo and set_foo.This is because you have some attributes for which there will be a get_foo but no set_foo so the example was that a foo sub that is both a getter and setter has no way to indicate that you can not actually set foo, because if it dies then you have to wrap all your get/setters in eval { ... } and nobody wants that.

    A good point, but I think you can make the case even more simply: undef is a useful value, and sometimes you want to set something to undef. If you use undef to indicate you want to do a get rather than a set, then you're stuck. So yeah, explicit getters & setters are good, and mutators aren't worth the saving of a few lines of code.

    Myself, I'm inclined to name my setters "set_*" but to avoid using the prefix "get_" on my getters. I think that reads a little more naturally and helps to distinguish between the two:

    my $attribute = $self->attribute; $self->set_attribute( $attribute );

      Here's an accessor/mutator that is set up for chaining and will accept undef as a value.

      sub foo { my $self = shift; # Set attribute if any args if ( @_ ) { $self->{foo} = shift; #validation is for chumps return $self; } else { return $self->{foo} } die "Whiskey Tango Foxtrot"; } # cotrast with this version, which I think you had in mind sub undefoo { my $self = shift; my $newfoo = shift; if ( defined $newfoo ) { $self->{foo} = $newfoo; # What, me validate input? return $self; } else { return $self->{foo}; } die "Whiskey Tango Foxtrot"; }
      print $obj->foo(undef), "\n"; # Prints object stringification print $obj->undefoo(undef), "\n"; # prints a "\n"

      I don't know how I feel about chaining mutators. I like accessor/mutators that always return the attribute value.


      TGI says moo

      Myself, I'm inclined to name my setters "set_*" but to avoid using the prefix "get_" on my getters

      But then your getters look exactly like the mutators you're trying to avoid! infact, your getters have exactly the same surprise toy inside!

      my $foo = Foo::Doom->new(); $foo->bar( 'shoe' ); # OH NOES! its just a getter # but it still looks right -_- # whereas my $f00 = Foo::f00li5h->new( in_accordance_with => TheDamian ); $f00->get_bar( 'shoe' ); # what the hell is 'shoe' there for? # this is clearly a getter

      Sure if you've got a bundle of other setters called, as set_foo in exactly the same place that you're adding the new code to set bar, you're a little less likely to write $foo->bar('shoe'), but only a litte.

      Personaly, I'd expect $foo->bar to be a mutator.

      @_=qw; ask f00li5h to appear and remain for a moment of pretend better than a lifetime;;s;;@_[map hex,split'',B204316D8C2A4516DE];;y/05/os/&print;
        But then your getters look exactly like the mutators you're trying to avoid!

        Not if you don't have any mutators, they don't.

        And since mutators are now deprecated, I don't see any point in letting them have the cleaner syntax.

        If I were working with a group of people I needed to ween away from mutators, I might have my getters warn if @_ isn't empty.

        if (@_) { carp "attempt to pass values to a getter"; }

        There's another issue with my naming convention, though:

        $attribute = $self->attribute; $attribute = $self->{attribute};
        With hash-ref based objects, cheating and peeking at the hash value looks an awful lot like using the getter correctly (or course, there'd be the same problem if it were a mutator). It could be argued that this would be more visually distinct:
        $attribute = $self->get_attribute; $attribute = $self->{attribute};

        Myself, I think what's needed is some sort of test code that looks for code that cheats and uses the object like a hash ref, but that's a project I keep putting off.

        (And personally, I'm holding off on "inside out objects" for now. I halfway expect that in a few years Damien Conway will announce that his "Class::Std" module is now deprecated, just like a lot of stuff in his "Object Oriented Perl" is now supposed to be deprecated, e.g. mutators.)