Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

Re^3: Tutorial: Introduction to Object-Oriented Programming

by adrianh (Chancellor)
on Dec 12, 2002 at 22:02 UTC ( #219441=note: print w/replies, xml ) Need Help??

in reply to Re: Re: Tutorial: Introduction to Object-Oriented Programming
in thread Tutorial: Introduction to Object-Oriented Programming

Abigail-II will be better able to comment than myself, but I've been playing around with her technique on-and-off since I discovered it on perlmonks. While I've not been doing it in anger, it's not been entirely in jest either. I've been re-writing some Test::Class hierarchies that were fairly deep, and hence possible sources for hash key clashes.

In general I've not had problems. It's a very nice hack to get around perl's lack of object attribute encapsulation. I'll probably use the technique in the next production project I do.

Abigail-II - have you considered writing a little tutorial on the subject? :-)

Issues that can cause problems:

  • You have to take overloading the "" operator into account. Since the attribute objects are indexed by "$self", you have to use overload::StrVal something like this:

    package Foo; use strict; use warnings; use overload; my %foo = (); sub new { my $class = shift; bless [], $class; }; sub foo { my $self = overload::StrVal shift; $foo{$self} = @_ if @_; $foo{$self}; }; sub DESTROY { my $self = overload::StrVal shift; delete $foo{$self}; };

    if there is any chance that somebody will write a sub-class that overrides "", for example:

    package Foo::Pretty; use base qw(Foo); use overload q{""} => sub { my $self = shift; my $foo = defined($self->foo) ? $self->foo : 'unde +f'; "<foo=$foo>"; }, ;
  • Having to remember to add/remove attribute hashes from the DESTROY method can be a pain. I tend to move attributes around classes a fair bit during refactoring. Forget to add an appropriate delete line to the DESTROY method and you suddenly have a nasty little memory leak. I've had this happen to me more often in the few weeks I've been playing with this technique than I've had hash/method name clashes in the several years I've been writing OO perl - but that may be lack of practice :-)

  • Object serialisation and other reflective acts become harder. No more quick'n'dirty throwing your object at Storable to serialise your object. No more throwing your object at Data::Dumper to get a snapshot of its state. This could be considered a feature depending on your point of view.

  • I've not had the chance to try teaching this method to perl/OO novices. My hunch is that it will turn out harder than hashes. Perl novices are already used to stuffing stuff in hashes, so it's a natural progression. OO novices latch onto the idea that an object is a "thing". My guess is that a single "thing" spread over several hashes goes against the naive concept of what an object is, and will therefore be harder to teach.

  • Having to list all of the attributes in the DESTROY method, as well as declare them at the top offends my sense of once and only once.

Replies are listed 'Best First'.
Re^4: Tutorial: Introduction to Object-Oriented Programming
by adrianh (Chancellor) on Dec 13, 2002 at 08:32 UTC

    Another issue has just come to mind. With the object attributes being keyed on "$self", which includes the name of the class, you can no longer bless an object into a sibling class to implement state transitions.

    Again, I guess some people would consider this a feature. I quite like the technique myself :-)

    To overcome you'll need to strip out the class name. Assuming nobody changes the output format of references in later perl versions something like this should work.

    package Foo; use strict; use warnings; use overload; my %foo = (); my %self = (); # we access self enough for it to be worth caching sub self { my $self = overload::StrVal shift; return $self{$self} if exists $self{$self}; $self{$self} = substr($self, index($self, '=')+1); }; sub new { my $class = shift; bless [], $class; }; sub foo { my $self = shift->self; $foo{$self} = @_ if @_; $foo{$self}; }; sub DESTROY { my $self = shift->self; delete $foo{$self}; undef %self; };

    Also, there is another advantage to Abigail-II's method. Storing the attributes in separate hashes should less expensive in memory - buckets cost less than hashes. Might make a difference if you're creating lots of objects.

      There's an easier way to strip out the class name. A reference in numeric context returns just the number part - so you could do my $self = 0 + shift;.


        Your right - forgotten about that.

        You still have the problem of overloading tho - what if a subclass overloads "+"?

        Unfortunately, there isn't an overload::NumVal to match overload::StrVal (unless I'm missing something somewhere ;-)

        You can get around this by blessing the object into another class you know isn't overloaded, get the numeric ID, and then bless the object back into it's original class. For example:

        package Foo::Base; use strict; use warnings; sub self { my $self = shift; my $package = ref($self); bless $self, __PACKAGE__; my $id = $self+0; bless $self, $package; return($id); }; package Foo; use base qw(Foo::Base); use strict; use warnings; my %foo = (); sub new { my $class = shift; bless [], $class; }; sub foo { my $self = shift->self; $foo{$self} = @_ if @_; $foo{$self}; }; sub DESTROY { my $self = shift->self; delete $foo{$self}; };

        Mildly evil - but seems to work.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://219441]
[Corion]: Oooh - I had another devious idea - "Host C" - a C language where every struct is 4K in size. This makes memory and disk access incredibly fast ;)

How do I use this? | Other CB clients
Other Users?
Others perusing the Monastery: (2)
As of 2017-04-25 08:53 GMT
Find Nodes?
    Voting Booth?
    I'm a fool:

    Results (449 votes). Check out past polls.