Re: Creating dynamic parent/child relationships
by Corion (Patriarch) on Sep 03, 2019 at 09:17 UTC
|
Don't use inheritance for this.
Consider having an array of "modificators" or "plugins" (for lack of detail) that the main class calls:
my $foo = One->new_from_config('myconfig.yml');
print Dumper $foo->frobnicate($bar);
sub One::frobnicate( $self, $item ) {
for my $step (@{ $self->steps }) {
$item = $step->frobnicate( $item );
};
return $item
}
The One::frobnicate method dispatches the call to all the listed ->frobnicate methods in ->steps. The ->steps array is filled with classes (or instances) from the configuration.
See also Module::Pluggable, which can load plugins. | [reply] [d/l] [select] |
|
package Base ;
sub new {
my $class = shift;
bless {}, $class;
}
sub do {
my $s = shift;
print "hi Base here\n";
}
package One ;
use parent 'Base';
sub do {
my $s = shift;
$s->SUPER::do;
print "hi from pkg one\n";
}
package Two ;
use parent 'One';
sub do {
my $s = shift;
$s->SUPER::do;
print "hi from pkg two\n";
}
package Three ;
use parent 'Two';
sub do {
my $s = shift;
$s->SUPER::do;
print "hi from pkg Three\n";
}
And then in a script:
my $three = Three->new;
$three->do;
This, of course, outputs:
hi Base here
hi from pkg one
hi from pkg two
hi from pkg Three
| [reply] [d/l] [select] |
|
package Base;
use One;
use Two;
use Three;
sub do {
print "hi from Base\n";
Three::do;
Two::do;
One::do;
}
And my config file would determine which order to run the subroutines from each of the packages. Is that right?
UPDATE: Here is a version of the code above that is a bit more dynamic:
package Base ;
use One;
use Two;
use Three;
sub new {
my $class = shift;
bless { steps => [ qw(Three Two One) ] }, $class;
}
sub do {
my $s = shift;
print "hi from Base\n";
for my $step ( @{ $s->{steps} } ) {
$step->do;
}
}
So I think I got it from here. I just have to dynamically import the correct modules and populate the steps attribute with the config file during construction. Nice trick. Thanks!
| [reply] [d/l] [select] |
|
package Base ;
sub new {
my $class = shift;
for my $mod (@_) {
eval "require $mod";
}
bless { steps => \@_ }, $class;
}
sub do {
my $s = shift;
print "hi from Base\n";
for my $step ( @{ $s->{steps} } ) {
$step->do;
}
}
Then call construct new from a script:
my $base = Base->new( qw ( One Three Two ) );
$base->do;
| [reply] [d/l] [select] |
Re: Creating dynamic parent/child relationships
by hippo (Bishop) on Sep 03, 2019 at 09:16 UTC
|
| [reply] |
|
| [reply] |
|
use strict;
use warnings;
package Foo;
use Moo;
package Trapezist;
use Role::Tiny;
sub swing { print "Whoooosh!\n"; }
package Trumpeter;
use Role::Tiny;
sub swing { print "1, 2, ... 1, 2, 3, 4, Hit it!\n"; }
package main;
my $person1 = Foo->new;
Role::Tiny->apply_roles_to_object ($person1, 'Trapezist');
my $person2 = Foo->new;
Role::Tiny->apply_roles_to_object ($person2, 'Trumpeter');
$person1->swing;
$person2->swing;
| [reply] [d/l] |
|
|
|
|
|
| [reply] |
|
|
|
Re: Creating dynamic parent/child relationships
by haj (Vicar) on Sep 03, 2019 at 10:31 UTC
|
I don't think I understand how you want to insert a fourth package "sometimes, but not always" in the hierarchy of package Three. Is this for different programs using the same package Three, for different runs of the same program, for different modules in one program which use package Three - or for different objects within the same program? Does the decision happen when you write your code, or at compile time, or at run time?
If the decision happens at runtime on a per-object base: I once had such a problem and found Moose with its roles to be a suitable approach (because the program used introspection anyway). Thanks to its meta protocol, Moose allows to add roles (which would be your intermediate packages) to existing objects, so objects can "learn new tricks during runtime".
If the decision happens once for a program run, I'd go for a plugin mechanism as suggested by Corion in Re: Creating dynamic parent/child relationships.
| [reply] |
Re: Creating dynamic parent/child relationships
by LanX (Saint) on Sep 03, 2019 at 11:43 UTC
|
I consider this a not very wise approach. Others already explained why.
Anyway if you are looking for a footgun ...
@package::ISA is a global variable, you are free to dynamically mess with it.
Beware of multiple inheritance though.
| [reply] [d/l] |
Re: Creating dynamic parent/child relationships
by talexb (Chancellor) on Sep 03, 2019 at 17:12 UTC
|
May not be appropriate, but you can use the if pragma to conditionally load a module.
use if $some_condition, Three;
(That's page 1019 in the Fourth Edition of The Camel, in case you have a dead tree version, which I do.)
Alex / talexb / Toronto
Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.
| [reply] [d/l] [select] |
Re: Creating dynamic parent/child relationships
by jcb (Parson) on Sep 04, 2019 at 03:17 UTC
|
This is a point where you really need mixins if you want to stick with object inheritance, but maybe you should rethink whether "file sorter" IS-A File::Collector or whether File::Collector HAS-A "file sorter" (or many file sorters as the case may be).
The idea of subclassing File::Collector to add categories seemed neat at the beginning, but this is starting to get unmaintainable.
To do this with mixins, define a "final derived class" that inherits from File::Collector and any number of "mixin" classes that provide sorting categories, using a form of the _run_chain method you mentioned in Unable to turn off strict refs to invoke methods across the mixins instead of working up a SUPER:: chain as the earlier versions did. Your application can adjust the @...::ISA array in that derived class (which should be otherwise empty) according to configuration, as LanX mentioned. Rewriting inheritance chains at runtime, while possible in Perl, is getting very close to "thermonuclear footgun" on the maintainability scale, and is almost certainly best avoided.
| [reply] [d/l] [select] |
|
Yeah, I'm feeling a little lost in the weeds. Taking away inheritance looks like it will necessitate implementing a lot of hacks and ugliness in the code to get to work properly. I was hoping to make it dead simple to implement a plug-in. My module works well, as is, with the parent-child inheritances hard coded in to the classes. I'll keep toying with it and see what I can come up with. At a minimum, I'll at least learn a lot.
| [reply] |
|
| [reply] |
Re: Creating dynamic parent/child relationships
by Anonymous Monk on Sep 04, 2019 at 16:30 UTC
|
This definitely smells like an "XY Problem" that has probably already been solved – several good metaphors have already been suggested here. And the key decision factor is: "sometimes, but not always." Once the scenario has been more carefully defined, an existing stock solution should readily suggest itself, and from this an appropriate implementation in Perl. | [reply] |
|
| [reply] |