Reputation: 397
I've inherited Perl code similar to what I have shown below. Package contains common subroutines and does not import nor export namespace. This calling convention seems atypical. Refactoring is an option.
I'm interested in understanding the risks/side effects of explicitly naming the package when calling a subroutine within the current package. Thanks in advance.
package Util;
sub _step1 {
# <code>
}
sub _step2 {
# <code>
}
sub doWork {
Util::_step1();
Util::_step2();
}
1;
Upvotes: 0
Views: 112
Reputation: 107060
Interesting. I'm thinking how this would affect inheritance.
Imagine a child class of the Util
class that overrides the _step1
and _step2
method. If that class called the dowork
method. It would not call the child class' _step1
and _step2
methods, but the parent class' _step1
and _step2
method.
Function calls ignore inheritance. The module would need to do something like Util->_step1() or package->_step1() for inheritance to matter, and even then the package search would start with Util, not a child class. – Ven'Tatsu
Really? Seems simple enough to test.
I have two packages: Local::Util
and Local::Util::Child
defined in my program. Local::Util::Child is a child class of Local::Util
.
Class Local::Util
has the following constructors and methods defined:
new
: Create a new object of that class_step1
_step2
doWork
: This calls _step1
and _step2
with the Util::
prefix.doWork2
: This calls _step1
and _step2
without the Util::
prefix.doWork3
: This calls _step1
and _step2
with the __PACKAGE__
prefix.doWork4
: This called _step1
and _step2
with a $class
prefix taken from ref
.Class Local::Util::Child
only redefines the _step2
method.
Here's the program:
#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);
# This is our basic Local::Util object manipulation
my $util_obj = Local::Util->new;
say q("$util_obj" is a member of the ") . ref($util_obj) . q(" class);
print q($util_obj->_step1: );
$util_obj->_step1;
print q($util_obj->_step2: );
$util_obj->_step2;
# This is a child class object of the above
my $uc_obj = Local::Util::Child->new;
say q("$uc_obj" is a member of the ") . ref($uc_obj) . q(" class);
# Calls to straight forward methods
print q($uc_obj->_step1: );
$uc_obj->_step1;
print q($uc_obj->_step2: );
$uc_obj->_step2;
# Now calls to methods that call other methods
say qq(\n=====\$util_obj->doWork=====);
$util_obj->doWork;
say qq(\n=====\$uc_obj->doWork=====);
$uc_obj->doWork;
say qq(\n=====\$util_obj->doWork2=====);
$util_obj->doWork2;
say qq(\n=====\$uc_obj->doWork2=====);
$uc_obj->doWork2;
say qq(\n=====\$util_obj->doWork3=====);
$util_obj->doWork3;
say qq(\n=====\$uc_obj->doWork3=====);
$uc_obj->doWork3;
say qq(\n=====\$util_obj->doWork4=====);
$util_obj->doWork4;
say qq(\n=====\$uc_obj->doWork4=====);
$uc_obj->doWork4;
###################################################
# Package Local::Util
#
package Local::Util;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
}
sub _step1 {
say "I'm calling Local::Util::_step1";
}
sub _step2 {
say "I'm calling Local::Util::_step2";
}
sub doWork {
Local::Util::_step1();
Local::Util::_step2();
}
sub doWork2 {
_step1();
_step2();
}
sub doWork3 {
__PACKAGE__->_step1();
__PACKAGE__->_step2();
}
sub doWork4 {
my $self = shift;
my $class = ref $self;
$class->_step1();
$class->_step2();
}
#
#############################################
#############################################
# Package Local::Util::Child
#
package Local::Util::Child;
use base qw(Local::Util);
sub _step2 {
say "I'm calling Local::Util::Child::_step2";
}
And, here's the output:
"$util_obj" is a member of the "Local::Util" class
$util_obj->_step1: I'm calling Local::Util::_step1
$util_obj->_step2: I'm calling Local::Util::_step2
"$uc_obj" is a member of the "Local::Util::Child" class
$uc_obj->_step1: I'm calling Local::Util::_step1
$uc_obj->_step2: I'm calling Local::Util::Child::_step2
=====$util_obj->doWork=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2
=====$uc_obj->doWork=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2
=====$util_obj->doWork2=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2
=====$uc_obj->doWork2=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2
=====$util_obj->doWork3=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2
=====$uc_obj->doWork3=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2
=====$util_obj->doWork4=====
I'm calling Local::Util::_step1
I'm calling Local::Util::_step2
=====$uc_obj->doWork4=====
I'm calling Local::Util::_step1
I'm calling Local::Util::Child::_step2
Interesting. You're right that it makes no difference whether or not I put the Util::
prefix, it still calls the parent method and not the child's method. Putting the __PACKAGE__
prefix does the same (which I thought how you're suppose to do it in order to make sure you're calling the current class's definition). The only way I could get the child to work is to use the $class
prefix which I had to get from the ref
command.
So, it seems if you call a method in another method, it will default to that class's method and not the child's method. I guess this makes sense -- especially since it looks like _step1
and _step2
are private methods that shouldn't be accessed outside of the parent method.
Upvotes: 3
Reputation: 385996
That's fine. The only down side is that you might forget to rename the sub calls if you rename the module.
But it's an odd thing to want to do. If you tell us the reason you want to do this, we can perhaps provider a better way of doing it.
Upvotes: 0