将大型模块划分为多个文件

时间:2012-06-14 06:57:57

标签: perl

道歉,看起来我的原始问题无法正确解释我在做什么以及我想要实现什么。这是一个更新的问题。

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

我有一个很大的Perl模块(比如ABC.pm),当我们添加新功能时,它会不断增长。大多数这些功能(几乎90%)发送请求和处理响应。以下是一个此类请求的代码。

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

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

拥有大文件变得难以维护。所以,我正在寻找一些最佳实践来使这更容易。我想到的一个想法是将这个大模块拆分成多个文件(如何??)

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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子例程。”


UPDATE :如果您必须保留原始子名称,因为遗留代码调用它们无法重构,您可以自动生成它们(使用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
}

答案 2 :(得分:0)

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

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'})

或其他任何要求。

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