6

Perl でストレージ関連のアプリを作成する必要があります。アプリは、ローカル マシンから他のストレージ ノードにファイルをアップロードする必要があります。現在、アップロード方法は FTP ですが、将来的にはビットトレントまたは未知のスーパーファイル転送方法になる可能性があります。

アップロードが必要なファイルごとに、ファイル名、ファイルのアップロード先のストレージ ノード、およびアップロード中に使用する転送方法を定義する構成ファイルがあります。

もちろん、次の方法を使用して問題を解決できます。

{
  if ( $trans_type == "ftp" ) { ###FTP the FILE}
  if ( $trans_type == "bit" ) { ###BIT the FILE}
  ### etc ###
}

しかし、学校で学んだ基本的な OO の知識をもってしても、これは良い設計ではないと感じています。(質問のタイトルは少し誤解を招く可能性があります。OO 以外のソリューションで私の問題を適切に解決できると思われる場合は、それで問題ありません。OO の知識が限られているため、実際にはより良いでしょう。)

では、一般的なアドバイスをいただけますか?もちろん、サンプルコードも提供していただけると大変助かります。

4

7 に答える 7

13

まず、Perlでの文字列の同等性テストはeq、ではなく==です。

名前付きビットやftpなど、作業を行う方法がある場合は、

my %proc = (
    bit => \&bit,
    ftp => \&ftp,
);

my $proc = $proc{$trans_type};
$proc->() if defined $proc;
于 2009-08-15T08:42:53.247 に答える
8

これにはハッシュを使用できます...

  1. 各転送メソッドにそれ自体をハッシュに登録させます。このOOは(転送メソッドファクトリでメソッドを呼び出すことによって)または手続き的に(ハッシュをパッケージ変数にするか、モジュール化したくない場合はメインパッケージに入れることもできます)行うことができます。

    package MyApp::Transfer::FTP;
    $MyApp::TransferManager::METHODS{ftp} = \&do_ftp;
    sub do_ftp { ... }
    1;
    
  2. 各転送メソッドは一貫したAPIを使用します。多分それはそれだけの関数かもしれませんし、あるいはそれはオブジェクトインターフェースかもしれません。

  3. ハッシュを介して転送を呼び出します。

    sub do_transfer {
        # ...
        my $sub = $MyApp::TransferManager::METHODS{$method}
            or croak "Unknown transfer method $method";
        $sub->($arg1, $arg2, ...);
        # ...
    }
    

ところで:OOレジスタメソッドは次のようになります。

package MyApp::TransferManager;
use Carp;
use strict;

my %registered_method;

sub register {
    my ($class, $method, $sub) = @_;

    exists $registered_method{$method}
        and croak "method $method already registered";

    $registered_method{$method} = $sub;
}

# ...

1;

(このコードはいずれもテストされていません。セミコロンの欠落はご容赦ください)

于 2009-08-15T08:44:35.507 に答える
6

ここでの正しい設計は工場です。これをどのようにDBI処理するかを見てください。TransferAgent最終的には、任意の数のクラスの1つをインスタンス化するクラスになりTransferAgent::*ます。明らかに、以下の実装が提供するよりも多くのエラーチェックが必要になります。このようなファクトリを使用すると、コードを追加または変更しなくても、新しいタイプの転送エージェントを追加できます。

TransferAgent.pm-ファクトリクラス:

package TransferAgent;

use strict;
use warnings;

sub connect {
    my ($class, %args) = @_;

    require "$class/$args{type}.pm";

    my $ta = "${class}::$args{type}"->new(%args);
    return $ta->connect;
}

1;

TransferAgent/Base.pmTransferAgent::*-クラスの基本機能が含まれています。

package TransferAgent::Base;

use strict;
use warnings;

use Carp;

sub new {
    my ($class, %self) = @_;
    $self{_files_transferred} = [];
    $self{_bytes_transferred} = 0;
    return bless \%self, $class;
}

sub files_sent { 
    return wantarray ?  @{$_[0]->{_files_sent}} : 
        scalar @{$_[0]->{_files_sent}};
}

sub files_received { 
    return wantarray ?  @{$_[0]->{_files_recv}} : 
        scalar @{$_[0]->{_files_recv}};
}

sub cwd    { return $_[0]->{_cwd}       }
sub status { return $_[0]->{_connected} }

sub _subname {
    return +(split "::", (caller 1)[3])[-1];
}

sub connect    { croak _subname, " is not implemented by ", ref $_[0] }
sub disconnect { croak _subname, " is not implemented by ", ref $_[0] }
sub chdir      { croak _subname, " is not implemented by ", ref $_[0] }
sub mode       { croak _subname, " is not implemented by ", ref $_[0] }
sub put        { croak _subname, " is not implemented by ", ref $_[0] }
sub get        { croak _subname, " is not implemented by ", ref $_[0] }
sub list       { croak _subname, " is not implemented by ", ref $_[0] }

1;

TransferAgent/FTP.pm-(モック)FTPクライアントを実装します。

package TransferAgent::FTP;

use strict;
use warnings;

use Carp;

use base "TransferAgent::Base";

our %modes = map { $_ => 1 } qw/ascii binary ebcdic/;

sub new {
    my $class = shift;
    my $self  = $class->SUPER::new(@_);
    $self->{_mode} = "ascii";
    return $self;
}

sub connect    { 
    my $self = shift;
    #pretend to connect
    $self->{_connected} = 1;
    return $self;
}

sub disconnect {
    my $self = shift;
    #pretend to disconnect
    $self->{_connected} = 0;
    return $self;
}

sub chdir { 
    my $self = shift;
    #pretend to chdir
    $self->{_cwd} = shift;
    return $self;
}

sub mode {
    my ($self, $mode) = @_;

    if (defined $mode) {
        croak "'$mode' is not a valid mode"
            unless exists $modes{$mode};
        #pretend to change mode
        $self->{_mode} = $mode;
        return $self;
    }

    #return current mode
    return $self->{_mode};
}

sub put {
    my ($self, $file) = @_;
    #pretend to put file
    push @{$self->{_files_sent}}, $file;
    return $self;
}

sub get {
    my ($self, $file) = @_;
    #pretend to get file
    push @{$self->{_files_recv}}, $file;
    return $self;
}

sub list {
    my $self = shift;
    #pretend to list remote files
    return qw/foo bar baz quux/;
}

1;

script.pl--TransferAgentの使用方法:

#!/usr/bin/perl

use strict;
use warnings;

use TransferAgent;

my $ta = TransferAgent->connect(
    type     => "FTP",
    host     => "foo",
    user     => "bar",
    password => "baz",
);

print "files to get: ", join(", ", $ta->list), "\n";
for my $file ($ta->list) {
    $ta->get($file);
}
print "files gotten: ", join(", ", $ta->files_received), "\n";

$ta->disconnect;
于 2009-08-15T08:55:22.943 に答える
3

Mastering Perlの動的サブルーチンのセクションにいくつかの例があります。

于 2009-08-15T17:07:44.437 に答える
2

ディスパッチ テーブルの実装MJD による HOP でのディスパッチ テーブルの扱いを参照してください。

于 2009-08-15T13:28:57.270 に答える
1

OOはやり過ぎでしょう。私の解決策はおそらく次のようになります。

sub ftp_transfer { ... }
sub bit_transfer { ... }
my $transfer_sub = { 'ftp' => \&ftp_transfer, 'bit' => \&bit_transfer, ... };
...
sub upload_file {
    my ($file, ...) = @_;
    ...
    $transfer_sub->{$file->{trans_type}}->(...);
}
于 2009-08-15T08:44:26.053 に答える
1

最初は FTP を使用し、後で他の転送方法に移行するとおっしゃいました。実際に 2 番目または 3 番目のテクノロジを追加する必要があるまで、「エレガント」にはなりません。その 2 番目の転送方法が必要になることはありません。:-)

「科学プロジェクト」としてやりたいのなら、素晴らしいことです。

オブジェクト指向の設計パターンが、決して解決しない問題の解決策を複雑にしているのを見るのはうんざりです。

最初の転送メソッドを uploadFile メソッドでラップします。2 番目のメソッドに if then else を追加します。3 番目の方法で洗練されたリファクタリングを行います。それまでに、ソリューションがおそらくかなり一般的なものになるのに十分な例が得られます。

もちろん、私の主なポイントは、2 番目と 3 番目の方法が必要になることは決してないということです。

于 2009-08-15T12:16:25.403 に答える