2

抱歉,看来我最初的问题无法正确解释我在做什么以及我想要实现的目标。这是一个更新的问题。

这可能是最简单的问题,但我无法在任何地方找到答案。

我有一个大型 Perl 模块(比如 ABC.pm),随着我们添加新功能,它每天都在增长。这些功能中的大多数(几乎 90%)发送请求并处理响应。这是一个此类请求的一些代码。

sub UserDeleteRequest
{
    my ($self, $inputParam) = @_;
    my $config = $self->getConfig();
    return $self->_doRequest (REQUEST => 'UserDeleteRequest',
                              PARAM => $inputParam));
}

与此类似,编写了其他函数,并且随着我们添加新请求而不断增长。

拥有大文件变得难以维护。因此,我正在寻找一些最佳实践来简化此操作。我想到的一个想法是将这个大模块拆分为多个文件(如何??)

4

3 回答 3

5

听起来您应该创建一个抽象类,您可以让其他类继承并覆盖它。以您相当模糊的示例为例,您的模块可能如下所示:

use strict;
package Foo;

sub new {

    my $class = ref(shift) || shift;
    my $self = {};
    bless( $self, $class );

    return $self;

}

sub processFooRequest {
    ...
}

sub processFooResponse {
    ...
}

然后,您可能有一个子类 Foo::Web,它可能看起来像这样:

use strict;
package Foo::Web;
use base "Foo";

sub processFooWebRequest {
    ...
}

sub processFooWebResponse {
    ...
}

在这种情况下,我没有使用新的构造函数,因为它继承自 Foo。我可以使用相同的名称保留这些方法,它们会简单地覆盖——实际上这可能是我应该做的——保持名称相同但改变内部功能。Foo 中定义的任何其他方法都将被继承。

你真的应该看看perlmod。一旦您开始了解它及其各种链接,您可能想看看Moose

于 2012-06-14T07:29:52.087 回答
2

如果潜艇真的非常相似,为什么不推广到一个潜艇呢?

my %validRequests = map {($_ => 1)} qq(UserDeleteRequest);

sub SendRequest {
    my ($self, $request, $inputParam) = @_;
    my $config = $self->getConfig();
    return undef unless $validReqiests{$request}; # If want to verify
    # 
    $inputParam = getInputParamDefault($request) unless $inputParam; 
    return $self->_doRequest (REQUEST => $inputParam,
                              PARAM => $inputParam));
}

如果某些类型的潜艇之间存在其他逻辑差异,您可以通过对继承进行适当的 OO 来解决它们,正如 Ilion 的回答所指出的那样;或者您可以采用更简单的方法,在更简单的情况下使用每个请求类型的帮助子引用哈希。

我添加了一个特殊的getInputParamDefault()调用 get 来解决您的评论“有时函数的调用者不提供 $inputParam 然后我们必须找到默认的调用者并将其传递给 _doRequest 子例程。”


更新:如果由于遗留代码调用它们而无法重构而必须保留原始子名称,则可以自动生成它们(使用 AUTOLOAD 或手动添加到命名空间):

# Code not tested.
my %requestSubNames = ("UserDeleteRequest" => "UserDeleteRequest");
foreach my $requestType (sort keys %requestSubNames) {
    no strict 'refs';
    my $subName = __PACKAGE__ . "::$requestSubNames{$requestType}";
    *{$subname} = sub { return $_[0]->SendRequest($requestType, $_[1]); };
        # Note - this may need to be closure-tweaked, it's 5am and I'm a bit asleep
    # Add to EXPORT/EXPORT_OK if needed
}
于 2012-06-14T08:40:02.667 回答
0

自动加载功能怎么样?这不是最有效的方法,但如果它们看起来都像示例并且没有真正做太多处理,那将为您节省所有相同的代码行。

package test;
use strict; use warnings;
our $AUTOLOAD;

sub AUTOLOAD {
  my $self = $_[0];
  # we DONT shift it, since the whole @_ is needed 
  # for the goto &$AUTOLOAD at the end

  if ($AUTOLOAD =~ /.*::(.*)/) {
    my $requestType = $1;
    return undef if $requestType eq 'DESTROY';

    $AUTOLOAD = sub {
      my ($self, $inputParam) = @_;
      my $config = $self->getConfig(); # Why do we need this?
      return $self->_doRequest (REQUEST => $requestType,
                                PARAM => $inputParam);
    };
    # This is NOT a 'goto LABEL' call. This goto calls the function we
    # just have created and passes the whole @_ as arguments to it.
    # See 'http://perldoc.perl.org/functions/goto.html' for details.
    goto &$AUTOLOAD;
  }
}

sub getConfig { return 1; }
sub _doRequest { my ($self, %foo) = @_; return $foo{REQUEST}; }
sub new { return bless {}, $_[0]; }

package main;
use strict; use warnings;
use Data::Dumper;
my $test = test->new;
print Dumper $test->foobar('nice param');

基本上,AUTOLOAD 功能会创建对您来说不存在的方法。在我们的例子中,它创建了一个匿名子,它发出请求类型的请求foobar并将参数'nice param'传递给它。

你也可以继续说

$test->getMilk('goat');
$test->isThereBeerLeftInTheFridge({ temp => 'cold', size => 'large'})

或有任何其他要求。

也许这是一个起点。如果所有请求都不断完成,这不是最快的解决方案。但是,它只会创建每个方法一次,然后在再次调用它时使用它。

于 2012-06-14T08:38:57.207 回答