Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Re: Re: Where/When is OO useful?

by Petruchio (Vicar)
on Jun 25, 2002 at 04:00 UTC ( [id://177024]=note: print w/replies, xml ) Need Help??


in reply to Re: Where/When is OO useful?
in thread Where/When is OO useful?

Where are you going to store your instance data?

Since you asked... in a variable outside the object itself: a closure, in all likelihood. In my experience, this is the best use for flyweight objects in Perl, far better than as an awkward method of strong encapsulation. Here is how I'd do it:

#!/usr/bin/perl use strict; use warnings; package BaseballPlayer; use Carp; my %attrib; sub BEGIN { %attrib = map { $_ => 1 } qw( RBI Batting_Average Hits Runs Stolen_Bases Games_Played ); no strict 'refs'; for my $n ( keys %attrib ) { *$n = sub { $_[0]->{$n} } } } sub new { my( $class, %arg ) = @_; exists $attrib{$_} or croak "Unknown stat: $_" for keys %arg; $arg{$_} ||= 0 for keys %attrib; bless \%arg, $class; } package BaseballPlayer::Pitcher; our @ISA = 'BaseballPlayer'; { my %object; sub new { my( $class, %arg ) = @_; my %pitcher_stat = map { $_ => delete $arg{$_} || 0 } qw( ERA Stri +keouts ); my $base = BaseballPlayer->new(%arg); my $ret = bless $base, $class; $object{$ret} = \%pitcher_stat; $ret; } sub ERA { $object{$_[0]}{ERA} } sub Strikeouts { $object{$_[0]}{Strikeouts} } sub DESTROY { delete $object{$_[0]} } } package main; my $p = BaseballPlayer::Pitcher->new( Hits => 23, ERA => 4.32 ); print $p->Hits, "\n"; print $p->ERA, "\n";

Likely you have your own solution; if it is significantly different (or especially if it's better) than mine, do share it. This solution passes the tests you mentioned, and has become a regular habit for me. I agree with your point: Perl doesn't make it easy to use inheritance. This is a wordy, tiresome ritual, and thus is error-prone. Various aspects of Perl's OO require such rituals, however; personally, I wouldn't single out inheritance on this account.

Update: Changed $p's ERA to something realistic, upon zdog's advice.

Update: Simplified my code, upon tye's advice. My inclusion of needless code was a cargo-cult imitation of my own practices, which reflected the needs of prior projects. This, I think, underscores my point about the unfortunate effects of rituals which compensate for the shortcomings of a language.

Replies are listed 'Best First'.
Re: Where/When is OO useful?
by Abigail-II (Bishop) on Jul 01, 2002 at 11:43 UTC
    I go one step further than "fly weight objects". I take a lexical hash for each attribute, and index those with the object. This means you never have to use string literals to access attributes (and hence you get the full benefit of using strict), and you only need one hash query instead of two to access the attribute.

    Here's my implementation of the BaseballPlayer::Pitcher class. It's totally independent of the implementation of the BaseballPlayer class (although it could be that you want to mask the constructor - but that's ok because the constructor is part of the API). I call this technique "Inside Out Objects" (people who saw my presentation at YAPC know all about it).

    package BaseballPlayer::Pitcher; { use vars '@ISA'; @ISA = 'BaseballPlayer'; my (%ERA, %Strikeouts); sub ERA : lvalue {$ERA {+shift}} sub Strikeouts : lvalue {$Strikeouts {+shift}} sub DESTROY { my $self = shift; delete $ERA {$self}, $Strikeouts {$self} } }

    Abigail

      delete $ERA {$self}, $Strikeouts {$self}

      That won't work like you expect it to.

      $ perl -le 'my(%one,%two);$one{k}=1;$two{k}=2;delete $one{k}, $two{k}; + print "one exists" if exists $one{k};print "two exists" if exists $t +wo{k}' two exists

      Nice technique though.

      -sauoq
      "My two cents aren't worth a dime.";
      
      Nice. Do you also have recommendations for keeping everything as neat and tidy while adding persistence? I've managed a few simple cases with Storable hooks, but dependencies on the ancestor class beyond just its api seem to be rearing their heads again :-(
        It shouldn't be too hard to write save and restore functions that dump the appropriate values of the hashes.

        And it's real easy to make class level save and restore functions that save/restore *all* the instances of a class.

        However, to do this right, you do need to cooperation of the classes you inherit....

        Abigail

      Another object idiom: this one, people tell me is aweful. I like it a lot because it overcomes something I hate about Perl: how object instance data requires lots of funny syntax. This syntax makes it tedious to refactor code from a subroutine into a method. Like Abagail's, it uses lexicals to good effect. It requires some glue, but class fields are then normal lexical variables. The object is a blessed hash - but instead of data, the hash holds the methods. The AUTOLOAD glue just dispatches to the correct hash element. The glue sets $self, so it is not necessary to read it off of the argument list manually, should it be needed (for a method call, for example).
      sub new { my $ERA; my $Strikeouts; bless { get_ERA => sub { $ERA }, set_ERA => sub { $ERA = shift }, get_Strikeouts => sub { $Strikeouts }, set_Strikeouts => sub { $Strikeouts = shift }, }, shift(); } sub AUTOLOAD { (my $method) = $AUTOLOAD =~ m/::(.*)$/; return if $method eq 'DESTROY'; our $this = shift; if(! exists $this->{$method}) { my $super = "SUPER::$method"; return $this->$super(@_); } $this->{$method}->(@_); } 1;
      Primary drawbacks are a small speed penalty - but for very small data classes like this, I tend to use rather ordinary blessed arrays, anyway, or psuedohashes, or "use fields" pragmatic module (all about the same thing right now - psuedohashes will be going away I'm told). Code inheritance works - however, fields declared lexically like this aren't inherited. All data is essentially private. I find this just as well - it forces a subclass to use its parents accessors. Finally, you have to be somewhat accustomed to Functional Programming to be used to staring at indenting like that. This is just a brief restatement of the second half of Anonymous Subroutine Objects at PerlDesignPatterns.com. I treat the AUTOLOAD footer as a Wrapper Module, and just use it into my namespace.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://177024]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (6)
As of 2024-04-16 09:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found