2

申し訳ありませんが、元の質問では、私が何をしていて、何を達成したいのかを正しく説明できなかったようです。これが更新された質問です。

これは最も簡単な質問かもしれませんが、どこにも答えが見つかりません。

私は大きなPerlモジュール(たとえばABC.pm)を持っており、新しい関数を追加するにつれて日々成長し続けています。これらの関数のほとんど(ほぼ90%)は、要求を送信し、応答を処理します。このようなリクエストのコードを次に示します。

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

これと同様に、他の関数が作成され、新しいリクエストを追加するにつれて成長し続けます。

大きなファイルを持つことは維持するのが難しくなっています。したがって、これを簡単にするためのいくつかのベストプラクティスを探しています。私が考えたアイデアの1つは、この大きなモジュールを複数のファイルに分割することでした(どのように??)

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

潜水艦が本当に非常に似ている場合は、1つの潜水艦に一般化してみませんか?

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

一部のタイプの潜水艦の間に他の論理的な違いがある場合は、Ilionの回答にあるように、継承を使用して適切なOOを実行することでそれらに対処できます。または、より単純なケースでは、リクエストごとのヘルパーサブ参照のハッシュを使用するというより単純なアプローチを実行できます。

私はあなたのコメントに対処するために特別な呼び出しgetを追加しましたgetInputParamDefault()。「関数の呼び出し元が$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

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

または他の要求があります。

多分これは出発点です。すべてのリクエストが絶えず行われる場合、これは最速のソリューションではありません。ただし、各メソッドは1回だけ作成され、後で再度呼び出された場合に使用されます。

于 2012-06-14T08:38:57.207 に答える