Tanktalus has asked for the wisdom of the Perl Monks concerning the following question:
Has anyone else had a desire to reverse inheritance? I don't just mean adding your module to some other module's @ISA - in perl, that's simple enough. I don't even entirely mean OO modules, either. It's more of a "replacement" of a module, such that when you think you're calling Foo->bar(), you actually get Baz->bar() instead. No source filters, either.
I have two such examples floating in my head... and want to expose them to some air, to see if this is simple enough to do and/or there is a simpler way to approach it.
The first example is a Getopt::Long wrapper. For me, this is a global object, so I don't really care about allowing a bunch of objects dealing with it, especially not different objects. Regular objects would be able to use derivation normally anyway.
In this case, what I would want is that any time someone calls Getopt::Wrapped->getOpt(), as an example, they really get MyOpts->getOpt() instead, if MyOpts is set up to replace Getopt::Wrapped. This isn't that big of a deal, except in a larger framework where I check parameters all over the place, all via Getopt::Wrapped->getOpt('foo'), even though the current script has overridden some things.
What I've kind of done here is create a $singleton object, allowing that when MyOpts->new is called, the object is blessed into MyOpts, and then stored for later use. But that only works because in my case, I never want to reuse this code for arbitrary @ARGV handling where I may have more than one derivation of Getopt::Long going at the same time in the same process.
I'd prefer that MyOpts.pm would look kind of like this:
package MyOpts; use Module::Replace 'Getopt::Wrapped', qw(new);
Then, when I call Getopt::Wrapped->new, I'd get MyOpts->new instead. In my theory, this "Module::Replace" would do the same thing as "use base", but would also substitute the &new in Getopt::Wrapped with the one in MyOpts. The tough part here would be how to call SUPER:: on a method, as I'm pretty sure that would break here. I think my workaround here would be that Module::Replace would rename &new to SUPER_new, which would work fine inside MyOpts - it's a module written based on Module::Replace, so slight changes in API would be ok.
If I want more than one derivation - one for global (using Module::Replace), and others for other parsing, the others simply would not replace. That would mostly work, depending on what the global derivation overrode.
The next example is far more convoluted, and is somewhere that I think something like Module::Replace is more useful, though I haven't done anything with this yet. Think of a set of objects that can create other objects in a many-to-many relationship. Now think that each of this objects are not of the base type, but derived. However, it's the base package that is doing the many-to-many instantiations. The base packages don't know what the derived packages are, but need to create them. I have two basic thoughts on this:
a) create a factory package. Mandate that the user of the base packages update the factory module to create the correct objects. Instead of trying to call Type->new(@args), I call Factory::create('Type_Name',@args), because I don't know what Type is (the user could be calling it My::Type). This just doesn't feel perlish - such a dynamic language shouldn't be constrained by this method. Mandating a factory also seems rather harsh for a language that doesn't even want to impose "private" methods on users ;-)
b) Mandate that the user of the base packages puts "use Module::Replace Base::Type => qw(new);" at the top of their package. Now in the base framework, I can just call Base::Type->new(@args) to get the right object created. Except for one minor detail: the new method can't just bless into whatever package specified. It would have to use __PACKAGE__. Of course, if there is any concept of it being further inherited from, then it would have to merely change: $class = __PACKAGE__ if $class eq 'Base::Type';
Any thoughts?
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Reversing (some of) inheritance
by jdporter (Paladin) on Apr 22, 2008 at 21:09 UTC | |
by Tanktalus (Canon) on Apr 22, 2008 at 21:55 UTC | |
by jdporter (Paladin) on Apr 23, 2008 at 14:13 UTC | |
|
Re: Reversing (some of) inheritance
by ursus (Acolyte) on Apr 22, 2008 at 21:56 UTC |