http://www.perlmonks.org?node_id=948735


in reply to Safer monkey-patching

n many cases, an easy alternative is to create a subclass of the class you are targeting and add your new methods there.
Yet, it doesn't solve the same problem you mention as a drawback to monkey patching.

Suppose you do subclass Data::Dumper (say, to My::Data::Dumper), and define a method My::Data::Dumper::print_r, and you use instances of My::Data::Dumper instead of instances of Data::Dumper. Now, what if a new version of Data::Dumper defines a method print_r, and calls that method in an OO way? Then in instances of My::Data::Dumper, My::Data::Dumper::print_r is called instead of the expected Data::Dumper::print_r. While not getting a warning or error at compile time (which you would get when monkey-patching), it doesn't seem to be likely that this is going to work out at runtime.

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".
And that's all honkey dory? Sorry, I don't see what's so good about this. Sure, you will not get a compile time error, but where you first would die with a bang if you call $error->asplode, that's unlikely to happen once Example::Error defines its own asplode.

I don't think there's a way to defend against future name clashes, assuming one party isn't aware of your pick of names. Of the three mentioned techniques (monkey patch, sub class, super class), the monkey patch will detect the name clash at compile time. The two OO variants will just silently do the wrong thing at run time.

Replies are listed 'Best First'.
Re^2: Safer monkey-patching
by tobyink (Canon) on Jan 19, 2012 at 10:30 UTC

    Redefining a function only produces a warning if "use warnings" is in effect. So classic monkey patching will not necessarily produce compile-time warnings.

    If a conflict warning is desired, that's fairly easy to add in with my method...

    BEGIN { require Example::Error; foreach my $method (qw/asplode/) { warn "Example::Error->$method already defined." if Example::Error->can($method); } push @Example::Error::ISA, __PACKAGE__; }
      Redefining a function only produces a warning if "use warnings" is in effect. So classic monkey patching will not necessarily produce compile-time warnings.
      Are you serious? You consider that a reasonable argument? "You shouldn't monkey patch because you may not get a warning on a name clash if you don't enable warnings"?

      I'm pretty sure that anyone who knows how to monkey patch knows about warnings.

      If a conflict warning is desired, that's fairly easy to add in with my method...
      Goody. Additional scaffolding, and you still aren't any further than what can be achieved with monkey-patching.

      Assuming Example::Error doesn't use AUTOLOAD to implement asplode (heh, if you want to consider monkey patchers that don't enable warnings, I will consider AUTOLOAD), it still doesn't solve anything. Once Example::Error implements asplode, you get a warning, and where your code expects to call your asplode, it calls Example::Error::asplode. Ergo, you haven't solved anything. It isn't safer than monkey-patching. With the extra scaffolding, it isn't unsafer either.