我继承了类似于下面显示的 Perl 代码。包包含公共子例程,不导入也不导出命名空间。这种调用约定似乎不典型。重构是一种选择。
我有兴趣了解在当前包中调用子例程时显式命名包的风险/副作用。提前致谢。
package Util;
sub _step1 {
# <code>
}
sub _step2 {
# <code>
}
sub doWork {
Util::_step1();
Util::_step2();
}
1;
我继承了类似于下面显示的 Perl 代码。包包含公共子例程,不导入也不导出命名空间。这种调用约定似乎不典型。重构是一种选择。
我有兴趣了解在当前包中调用子例程时显式命名包的风险/副作用。提前致谢。
package Util;
sub _step1 {
# <code>
}
sub _step2 {
# <code>
}
sub doWork {
Util::_step1();
Util::_step2();
}
1;
有趣的。我在想这将如何影响继承。
想象一个Util
覆盖_step1
and_step2
方法的类的子类。如果那个类调用了dowork
方法。它不会调用子类_step1
和_step2
方法,而是调用父类_step1
和_step2
方法。
函数调用忽略继承。该模块需要执行诸如 Util->_step1() 或package ->_step1() 之类的操作才能使继承变得重要,即使这样,包搜索也将从 Util 开始,而不是子类。– 文达苏
真的吗?似乎很简单,可以测试。
我有两个包:Local::Util
并Local::Util::Child
在我的程序中定义。Local::Util::Child 是Local::Util
.
类Local::Util
定义了以下构造函数和方法:
new
:创建该类的新对象_step1
_step2
doWork
:这调用_step1
和前缀。_step2
Util::
doWork2
: 这个调用_step1
并且_step2
没有Util::
前缀。doWork3
:这调用_step1
和前缀。_step2
__PACKAGE__
doWork4
:这称为_step1
and前缀取自_step2
.$class
ref
类Local::Util::Child
只重新定义_step2
方法。
这是程序:
#! /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";
}
而且,这是输出:
"$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
有趣的。你说得对,我是否放Util::
前缀都没有区别,它仍然调用父方法而不是子方法。放置__PACKAGE__
前缀也是如此(我认为您应该如何做以确保您正在调用当前类的定义)。我能让孩子工作的唯一方法是使用$class
我必须从ref
命令中获得的前缀。
因此,似乎如果您在另一个方法中调用一个方法,它将默认为该类的方法而不是子类的方法。我想这是有道理的——尤其是因为它看起来像是私有方法_step1
,_step2
不应该在父方法之外访问。
没关系。唯一的缺点是,如果重命名模块,您可能会忘记重命名子调用。
但这是一件奇怪的事情。如果您告诉我们您想要这样做的原因,我们也许可以提供更好的方法来做到这一点。