Perl: How to import routines from a base class?

I have a base class called Foo :: Base, I need to inherit its methods, for example, โ€œnewโ€, and import the subroutine names in the scope:

package Foo::Base; sub new { ... } sub import { no strict 'refs'; my $caller = caller; *{"${caller}::my_sub"} = sub { 1 }; } 1; 

So, I need to use this base class in my second class, Foo :: Child:

 use base 'Foo::Base'; 

... and it works for inheritance, but it does not import 'my_sub' into the scope. I can add a line

 use Foo::Base; 

for him, and it helps, but I don't want to write something like this:

 use base 'Foo::Base'; use Foo::Base; 

It looks weird ... Are there any suggestions on this issue?

+6
source share
3 answers

There are two reasons why you can do what you do, both of which are bad.

First you try to import methods from your parent class ... for some reason. You may have misunderstood how OO works. You do not need to do this. Just call the inherited methods as methods, and if these methods don't do something awkward, it will work fine.

Most likely, this is a mixed module, where some of them are methods, and some of them are imported functions. And for this you can do ...

 use base 'Foo::Base'; use Foo::Base; 

And you correctly noticed that it looks strange ... because it is rather strange. A class that also exports mixes idioms and this will lead to weird usage patterns.

Itโ€™s best to redo the class instead of exporting the functions, or to separate the functions into your own module, or make them class methods. If functions really do not have much to do with the class, then it is best to unscrew them. If they belong to a class, then make them class methods.

 use base 'Foo::Base'; Foo::Base->some_function_that_used_to_be_exported; 

This eliminates interface mismatch, and as a bonus, subclasses can override class method behavior like any other method.

 package Bar; use base 'Foo::Base'; # override sub some_function_that_used_to_be_exported { my($class, @args) = @_; ...do something extra maybe... $class->SUPER::some_function_that_used_to_be_exported(@args); ...and maybe something else... } 

If you don't have control over the base class, you can still make the interface smart by writing a subclass that turns exported functions into methods.

 package SaneFoo; use base 'Foo::Base'; # For each function exported by Foo::Base, create a wrapper class # method which throws away the first argument (the class name) and # calls the function. for my $name (@Foo::Base::EXPORT, @Foo::Base::EXPORT_OK) { my $function = Foo::Base->can($name); *{$name} = sub { my $class = shift; return $function->(@_); }; } 
+14
source

When you write use base , you use the facilities of the base module. And you pass it the module parameter that you want to be your base class.

For OO IS-A, no import is required. You call methods with an OO sample: $object_or_class->method_name( @args ) . Sometimes this means that you don't care who the supporter is, for example:

 sub inherited_util { my ( undef, @args ) = @_; ... } 

or

 sub inherited2 { shift; ... } 

However, if you want to use the utilities defined in the base module and inherit from the behavior of the class defined in this module, then this is exactly what the two operators indicate.

However, if you have two different types of behavior that you want to use in modules, it might be better to separate things like utilities into your own module and use them from both modules. In any case, explicit behavior is often better than implicit.

However, I used this template before:

 sub import { shift; my ( $inherit_flag ) = @_; my $inherit = lc( $inherit_flag ) ne 'inherit' ? 0 : shift() && !!shift() ? 1 : 0 ; if ( $inherit ) { no strict 'refs'; push @{caller().'::ISA'}, __PACKAGE__; ... } ... } 

that way, I make one call with an explicit set of usages.

 use UtilityParent inherit => 1, qw<normal args>; 
+6
source

To clarify the question of โ€œbut why not importing into Foo :: Child?โ€, Here is a program that tells you what is actually happening:

 use Foo::Child; print my_sub(); # Really? Yes, really! print Foo::Child::my_sub()," done\n"; 

and added this to the Foo :: Base :: import () procedure:

 print "Caller is $caller\n"; 

When you run this, you will see this as the output:

 Caller is main 1 Undefined subroutine &Foo::Child::my_sub called at foo_user.pl line 4. 

We see a report in Foo :: Base letting us know who the caller is: this is the main program! We see "1", which proves that yes, main :: my_sub now exists, and then fails because the import fell into the wrong namespace.

Why is this? Since the import process is processed by the main program. Foo :: Basic import is not called by anything in Foo :: Child; it is called by the main program during the loading of modules. If you really want to force subtitles, unlike methods, to import into Foo :: Child, you need to explicitly do this in Foo :: Child itself. "Use Foo :: Base" will do this, since it makes the import complete with Foo :: Child as the caller; if you insist on import, but double use removes you too much, you can call Foo :: Base :: import () immediately after the "use base". This is exactly what double use does.

However, I prefer Schwern class methods more and I recommend this alternative.

+1
source

Source: https://habr.com/ru/post/928136/


All Articles