使用 Method::Signatures 消除令人讨厌的变量分配
Perl的一个缺点是它的子程序和方法没有签名(忽略原型)。这意味着Perl开发者必须自己编写代码进行变量分配和类型检查,这会导致代码重复且冗长。本文展示了开发者如何通过使用Method::Signatures模块,永远消除这些样板代码。
func子程序
Method::Signatures导出了一个名为“func”的子程序,它可以替换“sub”内置函数。让我们看看一个典型的Perl子程序
use Carp qw/croak/;
sub extract_domain {
my $url = @_;
croak "Error missing argument URL $!" unless $url;
# code continues ...
}
我们可以使用“func”重构这些子程序,它接受一个签名(变量列表)
use Method::Signatures;
func extract_domain ($url) {
# code continues ...
}
将“sub”替换为“func”意味着它将声明$url,如果$_[0]不存在则croak,否则将其分配给$url。这消除了包含样板代码和检查代码的需要,在示例中这减少了代码长度的33%(2行代码)。
method子程序
Method::Signatures还导出了一个名为“method”的子程序,它可以替换面向对象代码中的“sub”。除了接受像“func”一样的签名参数之外,“method”还会自动声明和分配$self。考虑以下代码片段(来自Nginx::Log::Entry)
package Entry;
use Time::Piece;
use Nginx::ParseLog;
use HTTP::BrowserDetect;
sub new {
my ( $class, $log_line ) = @_;
die "Error: no log string was passed to new" unless $log_line;
my $self = Nginx::ParseLog::parse($log_line);
$self->{detector} = HTTP::BrowserDetect->new( $self->{user_agent} );
return bless $self, $class;
}
sub get_ip {
my $self = shift;
return $self->{ip};
}
sub get_timezone {
my $self = shift;
return substr( $self->{time}, -5 );
}
sub was_robot {
my $self = shift;
return $self->{detector}->robot;
}
sub get_status {
my $self = shift;
return $self->{status};
}
sub get_request {
my $self = shift;
return $self->{request};
}
1;
和重构后使用“method”的版本
package Entry_New;
use Time::Piece;
use Nginx::ParseLog;
use HTTP::BrowserDetect;
use Method::Signatures;
func new ($class, $log_line) {
my $self = Nginx::ParseLog::parse($log_line);
$self->{detector} = HTTP::BrowserDetect->new( $self->{user_agent} );
return bless $self, $class;
}
method get_ip {
return $self->{ip};
}
method get_timezone {
return substr( $self->{time}, -5 );
}
method was_robot {
return $self->{detector}->robot;
}
method get_status {
return $self->{status};
}
method get_request {
return $self->{request};
}
1;
通过使用“method”,我们能够从代码中移除所有样板声明和检查,减少代码长度近20%,并提高了可读性。
基准测试 Method::Signatures
使用 Method::Signatures 会带来显著的性能损失吗?我们可以通过比较本文前面提到的原始和重构的 Entry 类来测试性能影响。我们将使用Benchmark::Forking模块来提高基准测试的准确性。这是基准测试脚本
use Benchmark::Forking qw/cmpthese/;
use Entry;
use Entry_New;
open (my $LOG, '<', 'access.log');
my @log = <$LOG>;
cmpthese (100, {
Entry => sub { foreach (@log) {
my $entry = Entry->new($_);
$entry->get_ip;
$entry->get_timezone;
$entry->was_robot;
$entry->get_status;
$entry->get_request;
}
},
Entry_New => sub { foreach (@log) {
my $entry = Entry_New->new($_);
$entry->get_ip;
$entry->get_timezone;
$entry->was_robot;
$entry->get_status;
$entry->get_request;
}
},
});
该脚本将10000条条目的Nginx访问日志读取到@log中。对于Entry和Entry_New,它将测试初始化Entry对象并调用对象访问器方法的性能,每个条目测试100次。它对@log中的每个条目都这样做。运行基准测试脚本后得到以下结果
s/iter Entry_New Entry
Entry_New 1.65 -- -1%
Entry 1.63 1% --
这些结果表明,使用 Method::Signatures 只会带来1%的性能损失,考虑到它提供的功能,这似乎是非常有价值的。
附加功能
Method::Signatures还有很多其他功能,如命名和可选参数、类型检查、默认值和别名。有关更多详细信息,请查看出色的模块文档。
本文最初发布在PerlTricks.com。
标签
反馈
这篇文章有什么问题吗?通过在GitHub上打开一个问题或拉取请求来帮助我们。