使用 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

标签

David Farrell

David是一位职业程序员,他经常推文博客关于代码和编程艺术。

查看他们的文章

反馈

这篇文章有什么问题吗?通过在GitHub上打开一个问题或拉取请求来帮助我们。