简体   繁体   English

如何在Perl中实现调度表?

[英]How do I implement dispatch tables in Perl?

I need to write a storage related app in Perl. 我需要在Perl中编写一个与存储相关的应用程序。 The app needs to upload files from the local machine to some other storage nodes. 该应用程序需要将文件从本地计算机上载到其他一些存储节点。 Currently, the uploading method is FTP, but in the future it may be bittorrent or some unknown super-file-transferring method. 目前,上传方法是FTP,但将来它可能是bittorrent或一些未知的超文件传输方法。

For every file that needs to be uploaded, there is a configuration file which defines the file name, the storage node which the file will be uploaded to and what transferring method should be used during the uploading. 对于需要上传的每个文件,都有一个配置文件,用于定义文件名,文件将上传到的存储节点以及上传过程中应使用的传输方法。

Of course, I can use the following method to solve my problem: 当然,我可以使用以下方法来解决我的问题:

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

But even with my basic OO knowledge learned in school, I still feel that this is not a good design. 但即使我在学校学到了基本的OO知识,我仍然觉得这不是一个好的设计。 (The question title might be a little bit misleading. If you think my problem can solved gracefully with a non-OO solution, it's quite OK for me. Actually it will be better, since I have limited OO knowledge.) (问题标题可能有点误导。如果你认为我的问题可以通过非OO解决方案优雅地解决,那对我来说还是可以的。实际上它会更好,因为我的OO知识有限。)

So could you guys give me some advice in general? 那么你们一般可以给我一些建议吗? Of course, if you provide some sample code as well, this will be a great help. 当然,如果你提供一些示例代码,这将是一个很大的帮助。

First, string equality testing in Perl is eq , not == . 首先,Perl中的字符串相等测试是eq ,而不是==

If you have methods to do the work, say named bit and ftp, 如果你有方法来做这项工作,比如说bit和ftp,

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

my $proc = $proc{$trans_type};
$proc->() if defined $proc;

You can use a hash for this... 你可以使用哈希...

  1. Have each transfer method register itself in the hash. 让每个传输方法在哈希中注册自己。 You can do this OO (by calling a method on some transfer method factory) or procedurally (just make the hash a package variable, or you could even put it in the main package if you don't want to modularize). 您可以执行此OO(通过在某些传输方法工厂上调用方法)或在程序上(只需将哈希值设置为包变量,或者如果您不想模块化,甚至可以将其放在主包中)。

     package MyApp::Transfer::FTP; $MyApp::TransferManager::METHODS{ftp} = \\&do_ftp; sub do_ftp { ... } 1; 
  2. Each transfer method uses a consistent API. 每种传输方法都使用一致的API。 Maybe its just it a function, or it could be an object interface. 也许它只是一个功能,或者它可能是一个对象接口。

  3. Call the transfer through the hash. 通过哈希调用传输。

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

BTW: The OO register method would look something like this: 顺便说一句: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;

(None of this code is tested; please forgive missing semicolons) (此代码均未经过测试;请原谅丢失的分号)

The correct design here is a factory. 这里的正确设计是工厂。 Take a look at how the DBI handles this. 看看DBI如何处理这个问题。 You will wind up with a TransferAgent class that instantiates one of any number of TransferAgent::* classes. 最后,您将使用TransferAgent类来实例化任意数量的TransferAgent::*类。 Obviously you will want more error checking than the implementation below provides. 显然,您需要比下面提供的实现更多的错误检查。 Using a factory like this means that you can add new types of transfer agents without having to add or modify any code. 使用这样的工厂意味着您可以添加新类型的传输代理,而无需添加或修改任何代码。

TransferAgent.pm - the factory class: 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.pm - contains the base functionality of a TransferAgent::* class: TransferAgent/Base.pm - 包含TransferAgent::*类的基本功能:

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 - implements a (mock) FTP client: 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 - how to use TransferAgent: 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;

在动态子例程的章节中,我在Mastering Perl中有几个例子。

OO would be overkill. OO会有点矫枉过正。 My solution would probably look something like this: 我的解决方案可能看起来像这样:

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

You said initially it will use FTP and move to other transfer methods later. 你说最初它将使用FTP并稍后转移到其他传输方法。 I wouldn't get "elegant" until you actually need to add the second or third technology. 在你真正需要添加第二种或第三种技术之前,我不会“优雅”。 That second transfer method may never be required. 可能永远不需要第二种转移方法。 :-) :-)

If you want to do it as a "science project" then great. 如果你想把它作为一个“科学项目”那么好。

I am tired of seeing OO design patterns complicating solutions to problems that never arrive. 我厌倦了看到OO设计模式使解决方案变得复杂,从而无法解决问题。

Wrap the first transfer method in an uploadFile method. 在uploadFile方法中包装第一个传输方法。 Add an if then else for the second method. 为第二种方法添加if then else。 Get elegant and refactor on the third method. 在第三种方法上获得优雅和重构。 By then you will have enough examples that your solution will probably be pretty generic. 到那时,您将有足够的示例,您的解决方案可能非常通用。

Of course, my main point is that the second and third methods may never be required. 当然,我的主要观点是可能永远不需要第二种和第三种方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM