in reply to Re^6: tie for Perlish, encapsulated objects
in thread tie for Perlish, encapsulated objects

Methods like get_/set_ do not necessarilly correspond to any actual member variable of an object. Making FETCH and STORE procedures that handle every possible method call would mean nested if statements or a dispatch table for all the exceptions, saying something like "if they tried to set the shnizzle property, go run some special method." Or worse, a bunch of in-line code for every exception in one gigantic method. It's far less practical than simply writing method calls in the first place.
  • Comment on Re^7: tie for Perlish, encapsulated objects

Replies are listed 'Best First'.
Re^8: tie for Perlish, encapsulated objects
by Roy Johnson (Monsignor) on Nov 28, 2005 at 19:10 UTC
    You have clearly not understood anything I've written.
    Methods like get_/set_ do not necessarilly correspond to any actual member variable of an object
    I'm talking about an API. That was the main thrust of my original post. Actual member variables are supposed to be hidden, and they are hidden equally well by my method or yours.
    Making FETCH and STORE procedures that handle every possible method call would mean
    I didn't suggest that they should handle every possible method call. They are specifically for simple accessors. Any other methods would be written just as before.

    Caution: Contents may have been coded under pressure.
      Let me be more explicit. If you change something from a simple accessor that you reach through the tie methods like $foo{'bar'} = 7 to a method call, you will have to either change that calling code to something like tied($foo)->set_bar(7) or else put a special case in the FETCH method so that it calls "set_bar()" if the key is "bar". This would break the abstraction in the former case or compicate the code in the latter.

      UPDATE: as requested, an example, using your code. Say we change the definitions of get/set_foo in MyThing to something like this:

      sub set_foo { my ($self, $newfoo) = @_; $self->[1] = localtime(); # track last change $self->[0] = $newfoo; $self->_recalculate_bar(); } sub get_foo { $self->[2] = localtime(); # track last access return $_[0]->[0]; }
      After this change, the $clunker code using MyThing requires no changes at all. On the other hand, the tied HashAPI version, must change the accessing code from this:
      $slick->{foo} = 'Some value'; print $slick->{foo}, "\n";
      to this:
      $slick->set_foo('Some value'); print $slick->get_foo(), "\n";
      You could avoid needing this change by changing the FETCH and STORE, like this:
      sub STORE { my ($self, $key, $val) = @_; # assuming this class has more than just 'foo'... unless (grep { $_ eq $key } @CLASS_KEYS ) { croak (ref $self) . "has no $key member"; } if ($key eq 'foo') { $self->[1] = localtime(); # track last change $self->[0] = $val; $self->_recalculate_bar(); } else { # set normal keys somehow } } sub FETCH { my ($self, $key, $val) = @_; # assuming this class has more than just 'foo'... unless (grep { $_ eq $key } @CLASS_KEYS ) { croak (ref $self) . "has no $key member"; } if ($key eq 'foo') { $self->[2] = localtime(); # track last access return $self->[0]; } else { # get normal keys somehow } }
      That would quickly become cumbersome though.
        Because I only had one key, I took shortcuts over how I'd implement a more involved object. It's just the implementation, so it can be changed as needed. With a dispatch table, it's pretty obvious that there's a straightforward parallel for any changes you make in your system.
        sub STORE { my ($self, $key, $val) = @_; # An inline dispatch table ({ foo => sub { $self->[1] = localtime(); # track last change $self->[0] = $val; $self->_recalculate_bar(); }, # ...any other keys... }->{$key} or sub {croak (ref $self) . " has no $key member"} )->(); } sub FETCH { my ($self, $key, $val) = @_; ({ foo => sub { $self->[2] = localtime(); # track last access return $self->[0]; }, # ...any other keys... }->{$key} or sub {croak (ref $self) . " has no $key member"} )->();

        Caution: Contents may have been coded under pressure.