What is monkey-patching? It's the practise of adding methods to somebody else's class. Perl makes this very easy:

package Data::Dumper::Extensions; use Data::Dumper; sub Data::Dumper::print_r { print Dumper(@_); }

... but monkey patching is generally looked down upon. There are many good reasons - chiefly, what if a new version of Data::Dumper introduces its own print_r method? Worse still, what if the maintainers of Data::Dumper are discouraged from adding print_r because of worries of causing incompatibilities with your module?

The example above is somewhat contrived. There's not really any reason to define the print_r function in Data::Dumper - it could just as easily be in a separate package. Monkey patching becomes more useful when it's used to define new methods for object-oriented modules.

What is the alternative to monkey patching? In many cases, an easy alternative is to create a subclass of the class you are targeting and add your new methods there. Ain't OO great? Yes, except when it isn't.

The subclassing technique becomes less useful when you've got lots of objects which can instantiate each other. Say for example, you've got a class Example::ErrorList which is a list of Example::Error objects. Maybe you've subclassed Example::Error as My::AwesomeError... but Example::Error doesn't know this, and its most_recent_error method stubbornly returns plain old Example::Error objects.

This kind of situation makes monkey patching more attractive.

But how can we go about safer monkey patching? I think I've hit upon a good technique. Let's use the example of My::AwesomeError, which is a subclass of Example::Error, but adds an extra method asplode. (Example::Error already has a method called as_string.)

package My::AwesomeError; BEGIN { require Example::Error; push @Example::Error::ISA, __PACKAGE__; } sub as_string { die "as_string should be implemented by subclass"; } sub asplode { my $self = @_; die "BANG! ".$self->as_string; }

What we do here is set My::AwesomeError as a superclass or Example::Error. This ensures that $error->asplode will work on Example::Error objects, but also ensures that if Example::Error ever implements its own asplode method, that one will "win".

What do people think? Advantages? Drawbacks? Improvements?