gargle has asked for the wisdom of the Perl Monks concerning the following question:

I just bought Damian's Perl Best Practices and after reading I was left wondering about the 'best' way to do OO. I understand his reasoning about inside-out objects and the big "why it's better".

However, I always thought anonymous subs would also provide the same 'services' as inside-out objects...

Or am I really missing something? (this poor soul has to work with java code - it may have poisoned my mind :(

It seems to me the following is thread-save and encapsulates all the data...

package Rekening; # class for storing information about an account and doing a little qu +ick credit transaction use warnings; use strict; use Carp; sub new { my $class = shift; my $self = { ACCOUNT => undef, NAME => undef, BALANCE => 0, }; my $closure = sub { my $field = shift; if (@_) { $self->{$field} = shift; } return $self->{$field}; }; bless ($closure,$class); return $closure; } # public accessors sub account { &{ $_[0] }("ACCOUNT", @_[1 .. $#_]) } sub name { &{ $_[0] }("NAME", @_[1 .. $#_]) } sub balance { &{ $_[0] }("BALANCE", @_[1 .. $#_]) } # protected method printMe sub printMe { caller(0)->isa(__PACKAGE__) || confess "cannot call protected meth +od\n"; my $self = shift; print $self->account . " " . $self->name . " " . $self->balance . "\n"; } # private method add my $add = sub { my $self = shift; my $amount = shift; $self->balance($self->balance+$amount); }; # private method padd sub padd { my $self = shift; my $amount = shift; caller(0) eq __PACKAGE__ || confess "cannot call private method"; $self->balance($self->balance+$amount); } # public method credit sub credit { my $self = shift; my $amount = shift; $self->padd($amount); } 1;
--
if ( 1 ) { $postman->ring() for (1..2); }

Replies are listed 'Best First'.
Re: OO - inside-out or by means of a anonymous sub?
by ikegami (Patriarch) on Jan 12, 2006 at 16:08 UTC

    As I see things, the biggest advantage of using inside-out object is typo checking. $self->{attribute} becomes $attribute{$self}, which allows typos of attribute to be caught at compile time, rather than having them lead to weird problems at run-time. You mitigated this by providing accessors, but not by using closures.

    Inside-out objects also provide data protection. If the attribute hash is a my variable, neither the main program nor other modules can access the attributes without using the accessors. Howver, in your closure code, anyone can do &{ $obj }("BALANCE", '1000000');. True, sanity checks could be added inside the closure, but inside-out objects could ban access to BALANCE completely.

      $self->{attribute} becomes $attribute{$self}

      Minor nit: it should be $attribute{ refaddr $self }. Just plain $self will break if stringification is overloaded.

      Inside-out objects also provide data protection.

      As I see it, encapsulation is not synonymous with data protection. Encapsulation means providing a set interface for interaction such that other parts of the program do not need to take into account the internal implementation of the object. Regular Perl objects can only provide incomplete encapsulation, even with interface methods, because subclasses must use the same underlying data structure. If the parent class changes its implementation, the subclass will break. That's an encapsulation failure.

      Inside-out objects can provide complete encapsulation, but this is only true if the memory address is used directly as the index into the property data structures. Inside-out objects that cache an identifier or memory address inside a blessed scalar (as Class::Std does, for example) are also mandating a data structure and thus fail to completely encapsulate the implementation.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      My take on typo checking is that attributes should be accessed by variables (or constants) rather than by strings. Coincidentally, that makes indexing an arrayref just as convenient as indexing a hashref.

      $self->{attribute} becomes $self->[ATTRIBUTE] (where ATTRIBUTE is an integer constant). Those constants should not be visible outside of the package published or exported, because they're an implementation detail.

      Admittedly, the initialization code becomes a bit less beautiful, but it's still perfectly clear:

      sub new { my ($class) = @_; my @obj; @obj[ACCOUNT, NAME, BALANCE] = (undef, undef, 0); return bless( \@obj, $class); }
      Variations on this that avoid declaring an array would probably be uglier.

      Caution: Contents may have been coded under pressure.
        I use the method you describe as well. However, I'm curious how you hide the constants from other packages. ("Those constants should not be visible outside of the package") It could be done if they were lexical variables, but there's no $ on your constants.
Re: OO - inside-out or by means of a anonymous sub?
by revdiablo (Prior) on Jan 12, 2006 at 17:09 UTC
Re: OO - inside-out or by means of a anonymous sub?
by diotalevi (Canon) on Jan 12, 2006 at 19:01 UTC

    It isn't important to forcibly restrict your data this way. If you use a reglar object without fancy stuff, it'll work great and people will be able to maintain your code. You can't force other people to use your modules in a sane way anyway and you shouldn't try. Unless you have a good reason to want to use a non-standard object type, use something normal.

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: OO - inside-out or by means of a anonymous sub?
by Perl Mouse (Chaplain) on Jan 12, 2006 at 17:03 UTC
    How do you subclass using closures? That is, proper subclassing, with the subclass not needing to know the implementation of the superclass.
    Perl --((8:>*