1

ヘッダー付きのわずかに異なる形式のcsvファイルを解析し、ファイル内のデータを表すオブジェクトのリストを返すMooseクラスを作成しようとしています。コードの簡略版は次のとおりです。

package MyParser;

use Moose;
use namespace::autoclean;
use Text::CSV_XS;

use MyData;  #class that represents data for each row of csv

has 'type' => ( is => 'ro', isa => 'Str', required => 1 );

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

    open my $fh, '<', $file || die "Can't open file $!";

    my $csv = Text::CSV_XS->new;
    $csv->column_names($csv->getline($fh));

    my @data;
    if ($self->type eq 'filetype1'){
        while (my $row = $csv->getline_hr($fh)){
            push @data, MyData->new(field1 => $row->{col1},
                                    field2 => $row->{col2},
                                    field3 => $row->{col3},
                                    );
        }
    }
    elsif ($self->type eq 'filetype2'){
        while (my $row = $csv->getline_hr($fh)){
            push @data, MyData->new(field1 => $row->{colA},
                                    field3 => _someFunction($row->{colB}), # _someFunction does some manipulation with the data
                                    field5 => $row->{colC},
                                    );
        }
    }
    elsif ($self->type eq 'filetype3'){
        while (my $row = $csv->getline_hr($fh)){
            push @data, MyData->new(field1 => $row->{column_1},
                                    field2 => _someOtherFunction($row->{column_2}),  # _someOtherFunction does some manipulation with the data
                                    field3 => $row->{column_3},
                                    field4 => $row->{column_4},
                                    field5 => $row->{column_5},
                                    );
        }
    }
    close $fh;

    return \@data;
}

__PACKAGE__->meta->make_immutable;

1;

クラス MyData は単純なデータ構造であり、一部の属性にはデフォルトの属性があります (したがって、上記とは異なる初期化があります)。一部の csv ファイル タイプには、ファイル タイプに依存する何らかの操作が必要な列 (たとえば、単純な式に入れる必要がある数値) もあります。この MyData はメイン スクリプトに返され、Oracle のテーブルに挿入されます。

私の目的は、MyParser が、必要に応じて拡張できる特定の指定されたタイプの csv ファイルを処理し、get_data メソッドから MyData のリストを返すことです。ただし、現在の方法は、私が解決しようとしているものに対するエレガントでシンプルなソリューションのようには見えません。

だから私が尋ねたい/コメントしたいのは:

これを解決するためのより良い/より簡単な方法はありますか (おそらく Factory パターンなどの設計パターンを介して)?
それとも、単純に見えて複雑な問題を解決しようとしているのだろうか?

4

2 に答える 2

1

if-elsif-elsifコンストラクトで繰り返されるコードの代わりに、フィールド マッピング ルールを構成ファイルに入れると、よりクリーンになります。たとえば、次のようなデータ構造を使用します。

{
    filetype1 => {
        field1 => 'col1',
        field2 => 'col2',
        field3 => 'col3',
    },
    filetype2 => {
        field1 => 'colA',
        field3 => {
            function => sub {},
            params   => ['colB'],
        },
        field5 => 'colC',
    },
    filetype3 => {
        field1 => 'column1',
        field2 => {
            function => sub {},
            params   => ['column_2'],
        },
        field3 => 'column_3',
        field4 => 'column_4',
        field5 => 'column_5',
    },
};

次に、 if-elsif-elsifコンストラクトを次のようなものに置き換えることができます (マッピング ルールがロードされ、 に保存されていると仮定します$filetype_mappings)。

while (my $row = $csv->getline_hr($fh)) {
    my %my_data = map {
        my $m = $filetype_mappings->{$_};
        $_ => ( ref $m ? &{$m->{function}}(map {$row->{$_}} @{$m->{params}})
                       : $row->{$m}
        );
    } keys %$filetype_mappings;
    push @data, MyData->new(%my_data);
}

マッピング ルールを分離することで、新しいファイル タイプのサポートを追加したり、既存のファイル タイプを 1 か所で変更したりすることが容易になります。

于 2012-07-30T00:43:30.933 に答える
0

このようにすることは、それほど悪い考えではありません。物事をシンプルにしましょう!

OTOH コンストラクターから呼び出される「抽象」メソッド「parseData」を持つ MyData の基本クラスを作成できます。MyData、MyData などと言うことができ、すべてが parseData メソッドを実装しています。次に、get_data で次のようにします。

my($self, $file) = @_;

open my $fh, '<', $file || die "Can't open file $!";

my $csv = Text::CSV_XS->new;
$csv->column_names($csv->getline($fh));

my @data;
while (my $row = $csv->getline_hr($fh)){
    my $class = 'MyData'.$self->type;
    push (@data, $class->new($row));
}
close $fh;
return \@data;
于 2012-07-29T23:35:38.990 に答える