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

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.
  • Comment on Re^8: tie for Perlish, encapsulated objects

Replies are listed 'Best First'.
Re^9: tie for Perlish, encapsulated objects
by perrin (Chancellor) on Nov 28, 2005 at 20:07 UTC
    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.