Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Re: messing with @ISA - unblessing

by crashtest (Curate)
on Aug 02, 2008 at 16:05 UTC ( #701832=note: print w/replies, xml ) Need Help??


in reply to messing with @ISA - unblessing

I see your code and I think Decorator pattern. Basically, you create a class that wraps the original object, and dispatches all method calls to the original object, except for those you want to override/modify, or those that are new. The advantage here is if some other part of your code is referencing the object you are monkeying with, it won't suddenly find its behavior changed.

In Perl, you get the added bonus of being able to implement all the methods that just dispatch to the original object using AUTOLOAD. Below is an example I've used before, YMMV.

package MyDecorator; sub new{ my ($class, $wrapped, %options) = @_; my $self = { wrapped => $wrapped, options => \%options }; bless $self, __PACKAGE__; } sub method1{ my $self = shift; my $wrapped = $self->{'wrapped'} # Do something different than what # $wrapped->method1 would do. # ... } sub method2{ my $self = shift; my $wrapped = $self->{'wrapped'} # method2 is a new method that now provides additional # functionality for the object. } # Implement autoload to defer all the methods we can't handle to the # wrapped class. sub AUTOLOAD{ return if our $AUTOLOAD =~ /::DESTROY$/; my $self = shift; my ($method, @namespace) = reverse(split '::' => $AUTOLOAD); my $wrapped = $self->{'wrapped'}; if ($wrapped->can($method)){ no strict 'refs'; $AUTOLOAD->($wrapped, @_); } else{ die "Cannot run or defer method \"$method\"!"; } }

(All this goes to show, once again, TIMTOWTDI. But also: I am no expert on object oriented design, so take this with a grain of salt.)

Replies are listed 'Best First'.
Re^2: messing with @ISA - unblessing
by Your Mother (Bishop) on Aug 03, 2008 at 05:25 UTC

    Applying this, as I learned this week, is trivial with Moose (or Mouse). Here is a snippet that installs a Template object inside your object, and only when needed, and then dispatches--via the handles param which has even more sugar than shown--any calls on your $object->process to the TT2 object exactly as if it were called instead. It's really very hot.

    use Mouse; has "tt2" => ( is => "ro", lazy => 1, # not always used isa => "Template", default => sub { require Template; Template->new(); }, handles => [qw( process )], );

    Update (2017-07-11): moved use to require inside the default sub. A better practice if the object is not always used.

Re^2: messing with @ISA - unblessing
by grep (Monsignor) on Aug 03, 2008 at 16:28 UTC
Re^2: messing with @ISA - unblessing
by Boldra (Deacon) on Aug 03, 2008 at 09:55 UTC
    Many thanks for your suggestion, but AUTOLOAD only lets me define new methods, not redefine old ones, which is one of my aims.

      I don't understand how that's a problem. The AUTOLOAD goes in the Decorator class, not the original object's class. The Decorator is new, so I wouldn't expect it to subclass anything else...?

      Of course, you can implement a Decorator class without AUTOLOAD, it's just more tedious as you have to write a little method each time, and maintain the Decorator as the original object's class changes. Based off your example, maybe something like this:

      package Curse::Older; sub new{ my ($class, $player, $older_by) = @_; my $self = { player => $player, older_by => $older_by, cursed => 1 }; bless $self, $class; } # name is just simply delegated sub name{ my $self = shift; $self->{'player'}->name(@_); } # age is however intercepted and modified sub age{ my $self = shift; my $player = $self->{'player'}; my $age = $player->age(); if ($self->{'cursed'}){ $age + $self->{'older_by'}; } else{ $age; } } # Meanwhile... package main; my $player = new Player('Jim', 30); # curse Jim to make him 10 years older my $cursed_player = Curse::Older->new($player, +10);

      Anyway, it looks like you're going down the re-blessing route. The Java programmer in me thinks that's nuts, but the Perl hacker in me admires what the language lets you do!

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (4)
As of 2020-11-30 02:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?