2009-08-15 24 views
5

Perl'de depolama ile ilgili bir uygulama yazmam gerekiyor. Uygulamanın yerel makineden diğer bazı depolama düğümlerine dosya yüklemesi gerekiyor. Şu anda, yükleme yöntemi FTP'dir, ancak gelecekte bittorrent veya bazı bilinmeyen süper dosya aktarma yöntemi olabilir. Yüklenen gereken her dosya için Sevkıyat tablolarını Perl'de nasıl uygularım?

, dosya adı, dosya yükleme sırasında kullanılması gereken ve hangi aktarılması yöntemine yüklenecektir depolama düğümü tanımlayan bir yapılandırma dosyası vardır.

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

Ama temel OO bilgi okulda öğrenilen bile, hala bu iyi bir tasarım olmadığını düşündüğümüzden:

Elbette, benim sorunu çözmek için aşağıdaki yöntemi kullanabilirsiniz. (Eğer benim sorunum olmayan OO çözeltisi ile incelikle çözüldü sanıyorsan Soru başlığı Aslında ben OO sınırlı bilgiye sahip olduğundan, daha iyi olacak. Benim için oldukça Tamam. Biraz yanıltıcı olabilir.)

Bana genel olarak bazı tavsiyelerde bulunabilir misiniz? Tabii ki, bazı örnek kod da sağlarsanız, bu büyük bir yardım olacaktır.

cevap

13

Birincisi, Perl dize eşitlik test eq değil == olduğunu. Eğer işi yapmak için yöntemler varsa, bit ve ftp adında demek

,

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

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

Sadece ne olup bittiğine dair biraz daha fazla açıklama eklemenizi tavsiye ederim, ama yine de iyi bir cevap. –

+0

Tanımlanmaya gerek yoktur, çünkü yanlış değerlerin hiçbiri geçerli bir coderef değildir. Ayrıca, yöntem arama tablosunda bulunamazsa, bir uyarı vermelisiniz. Bir alternatif, tüm yöntemleri bir sınıfa koymak ve 'can'ı kullanmaktır. –

+0

@Sinan Ünür- $ trans_type eq "fronobulax?" Başka bir deyişle, beklemediği ya da beklemediği bir tip mi? – xcramps

1

OO boğucu geldiği. Benim çözüm muhtemelen böyle bir şey görünecektir:

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

aksi takdirde ben Perl '& ftp_transfer'' $ transfer_sub {ftp} 'ziyade alt yordam bir referansa tarafından döndürülen değeri atar düşünüyorsun, karma da değişmezler üzerinde' 'önce' & 'gerektiğine inanıyorum. –

+2

@Chris: \ & subname alt adaya bir başvuru döndürür. Perlref, "Referanslar Yapma" başlığına bakın. – derobert

+1

Bazı OO'lara sahip olmak çok nadiren aşırı bir parazit. Ve bu örnek OO-bilge çözülecektir. – innaM

8

Bunun için bir karma kullanabilirsiniz ...

  1. her transfer yöntemi karma kendini kayıt var. Sen prosedür ya da (bazı transfer yöntemi fabrikasında bir yöntemi çağırarak) Bu OO yapabilirsiniz (sadece hash bir paket değişkeni yapmak veya modülarize etmek istemiyorsanız bile ana pakette koyabilirsiniz).

    package MyApp::Transfer::FTP; 
    $MyApp::TransferManager::METHODS{ftp} = \&do_ftp; 
    sub do_ftp { ... } 
    1; 
    
  2. Her bir aktarım yöntemi tutarlı bir API kullanır. Belki de sadece bir işlevi, ya da bir nesne arayüzü olabilir.

  3. Çağrı karma yoluyla transferi. BTW

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

: OO kayıt yöntemi şöyle görünecektir:

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; 

(Bu kodun hiçbiri test edilir; eksik noktalı virgül affet) burada

+0

bir karma hala olası aktarım aracılarını listeleme sorunu var. Bu listeyi kodlamak için bir sebep yok. Sadece TransferAgent :: FTP, TransferAgent :: SCP, TransferAgent :: BitTorrent, vb. Oluşturunuz. Daha sonra bir sınıf, doğru sınıfın oluşturulmasından sorumlu olabilir. –

+2

@Chas. Owens: Listeyi nereden kodlarım? Her yöntem uygulaması kendini kaydetmekle sorumludur. Bir yapılandırma dosyasına sahip olmak oldukça kolay, hangi aktarım modüllerinin yükleneceğini belirtin (bu özelleştirme düzeyini istiyorsanız, örneğin, çok bağımlılık gerektiren bir modülü kapatmak istiyorsanız) ya da belirli bir dizindeki tüm .pm dosyalarını yükleyin. Bu büyü seviyesini istiyorsun) – derobert

+1

@derobert Bireysel dersler kendilerini nasıl çalıştırıyor? Birden çok sunucu türüne aktarılması gereken bir programım varsa, her türümü programımda ayrı bir 'use' ifadesi olarak belirtmem gerekir mi? Sınıflar, kullanılıncaya kadar kendilerini kaydedemezler. Bu, belirli bir programın hangi sınıfları kullanabileceğini (örneğin belirttiğiniz yapılandırma dosyası gibi) kodladığınız bir yer anlamına gelir. Bir sınıfa yalnızca ihtiyaç duyulduğunda istendiğinde, bu tür bir kodlamaya gerek yoktur. –

6

doğru tasarım bir fabrikadır. DBI'un bunu nasıl ele aldığına bir göz atın. Sen TransferAgent::* sınıfların herhangi bir sayıda birini başlatır bir TransferAgent sınıf ile uğrarsınız. Açıkçası, aşağıdaki uygulamadan daha fazla hata kontrolü isteyeceksiniz. Bu şekilde bir fabrika kullanmak, herhangi bir kodu eklemeniz veya değiştirmeniz gerekmeden yeni aktarım aracıları ekleyebileceğiniz anlamına gelir.

TransferAgent.pm - Fabrika sınıfı: bir (sahte) FTP istemcisi uygular: - -

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:

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 bir TransferAgent::* sınıfın taban işlevini içerir

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 nasıl kullanılır:

#!/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; 
+0

FTP sınıfında bu "base" TransferAgent "satırını kullanmak istediğinizi sanmıyorum. Özellikle fabrika bağlantı yönteminiz türetilmiş bir sınıfta çalışmayacağından (sınıfın yanlış değerini, hatta bunun yerine daha da kötü bir örnek alacaktır). Belki de 'need' ve' new' satırlarında '__PACKAGE__' kullanmak istediniz? – derobert

+0

Bunun için Sınıf :: Fabrika'yı da CPAN'dan kullanabilirsiniz. Oldukça küçük bir modül, ama uygulamak ve kullanmak çok kolay. –

+0

@derobert Evet, geç kalmıştı ve henüz uyumadım. Kalıp, temel işlevselliği elde etmek için ayrı bir sınıfa sahip olmalıdır (TransferAgent'ın fabrikaya ek olarak olmasını istediğim şey). Kodu düzelttim ve uyanık olduğumdan birazcık dışarı çıkardım. –

1

Başlangıçta FTP kullanacağını ve daha sonra diğer aktarım yöntemlerine geçeceğini söylediniz. İkinci veya üçüncü teknolojiyi eklemeniz gerekene kadar "zarif" olmazdım. Bu ikinci transfer metodu asla gerekli olmayabilir. :-)

sonra harika bir "bilim projesi" olarak yapmak istiyorum.

Ben asla sorunlara çözüm karmaşık hale OO tasarım desenleri görme yoruldum.

İlk aktarma yöntemini bir uploadFile yöntemine sarın. İkinci yöntem için bir tane daha ekle. Üçüncü yöntemde zarif ve refactor alın. O zamana kadar, çözümünüzün muhtemelen oldukça genel olacağına dair yeterli örneklere sahip olacaksınız. Tabii

, benim ana nokta, ikinci ve üçüncü yöntemler gerekli asla olmasıdır.

+3

I-will-make-it-nice-ikincisi yöntemiyle ilgili sorun, o kadar güzel olmanız gerektiğinde, hoş olmayan arayüzü kullanan bir sürü mevcut programın olmasıdır. Tabi ki, gelecekteki ihtiyaçları her zaman yapmanız gereken basit ihtiyaçlara karşı dengelemeniz gerekir. Bu durumda, fabrika tasarım deseni iyi anlaşılmıştır ve uygulanması oldukça basittir ve gelecek için güzel bir arayüz sağlayan çok az zaman kaybedersiniz. –

3

Dinamik değişmezler üzerinde bölümlerde Mastering Perl çeşitli örnekleri var.

İlgili konular