Perl: How to call a specific method with multiple inheritance?

I have a package in perl that uses two other packages as a base.

Parent1:

package Parent1; use strict; use warnings; sub foo { my $self = shift; print ("\n Foo from Parent 1 "); $self->baz(); } sub baz { my $self = shift; print ("\n Baz from Parent 1 "); } 1; 

Parent 2:

 package Parent2; use strict; use warnings; sub foo { my $self = shift; print ("\n Foo from Parent 2 "); $self->baz(); } sub baz { my $self = shift; print ("\n Baz from Parent 2 "); } 1; 

Child: This uses two parent packages.

 package Child; use strict; use warnings; use base qw(Parent1); use base qw(Parent2); sub new { my $class = shift; my $object = {}; bless $object,$class; return $object; } 1; 

Main:

 use strict; use warnings; use Child; my $childObj = new Child; $childObj->Parent2::foo(); 

Output:

  Foo from Parent 2 Baz from Parent 1 

My analysis:

It can be seen from the output that the child object is passed to the parent2 method foo and from this method foo. He makes a call to the baz method. First he checks the baz method in the Child package, because I call it with the child. Since the baz method is not in the child package, it checks the method in the base class. Parent1 is the first base class for Child. So he finds the method in Parent1 and calls the baz method for Parent1.

My question is:

Is it possible to call the baz method from Parent2 without changing the order of the base class in the child?

My expected result:

  Foo from Parent 2 Baz from Parent 2 

The above example is just an analogy of my real problem. I do not have access to change the base classes. I have only access to change the Child class. So, is it possible to change the child class so that it retrieves both methods from Parent2 without changing the order of the base classes?

Thanks!

+7
inheritance subroutine perl package
source share
3 answers

If you have access to change the base classes, you can do this by changing $self->baz() to Parent2::baz($self) , but you said you can't do this.

Since this is not an option, how do you feel about temporarily changing the order of base classes? In Perl, the list of base classes is actually an array named @ISA inside each package, so you can use local to create a localized copy of this array in a block:

 #!/usr/bin/env perl use strict; use warnings; use 5.010; package Parent1; sub foo { say 'foo1'; $_[0]->baz; } sub baz { say 'baz1'; } package Parent2; sub foo { say 'foo2'; $_[0]->baz; } sub baz { say 'baz2'; } package Child; use base qw( Parent1 Parent2 ); sub new { return bless {} } package main; my $childobj = Child->new; { local @Child::ISA = qw( Parent2 Parent1 ); say 'In localized block'; $childobj->Parent2::foo; } say 'Block has exited'; $childobj->Parent2::foo; 

Output:

 In localized block foo2 baz2 Block has exited foo2 baz1 

So, you can see that this provides the desired result inside the block with localized @ISA , and then the original behavior is restored when the block exits.

Also, a note at the end: Using new Child in Perl is called an "indirect object designation" and is usually considered Bad Thing . Instead, I recommend using Child->new .

+4
source share

You can override the necessary methods in the Child class by sending full calls

 package Child; ... sub baz { shift; Parent2::baz(@_); } ... 

When added to your code, it prints as desired, Baz from Parent2 .

This is a pretty manual way to β€œincrease” the interface of these classes, which were clearly not intended to be inherited multiple times. But the situation you are describing is really unpleasant, and you need to do something like this or hard-specific @ISA manipulations.

For more complex needs, see mro directly related to all of this. It supports some introspection, so you can make decisions at runtime. You still have to have Child::baz() for this.

Can you change the design in some way and not use multiple inheritance?

+4
source share

You can create an alias for your preferred method. This is usually a bad idea if you do not know what you are doing. Take a look at perldoc perlmod #Symbol Tables .

Update your main file to:

  use strict; use warnings; use Child; my $childObj = new Child; $childObj->Parent2::foo(); print "------\n"; no warnings; # Otherwise will complain about 'Name "Child::baz" used only once: possible typo' # Create an alias *Child::baz = *Parent2::baz; use warnings; $childObj->Parent2::foo(); 

Exit

 Foo from Parent 2 Baz from Parent 1 ------ Foo from Parent 2 Baz from Parent 2 
+1
source share

All Articles