1

我正在尝试编写一个 Moose 类,该类解析带有标题的格式略有不同的 csv 文件,并返回代表文件中数据的对象列表。这是代码的简化版本:

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 列表。但是,现在的方法似乎不是我要解决的问题的优雅/简单的解决方案。

所以我想问/评论的是:

有没有更好/更简单的方法来解决这个问题(可能通过工厂模式等设计模式)?
还是我想解决一些看起来很简单的事情,让事情变得非常复杂?

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

将映射规则分开应该可以很容易地添加对新文件类型的支持或在一个地方对现有文件类型进行更改。

于 2012-07-30T00:43:30.933 回答
0

这样做并不是一个坏主意。让我们保持简单!

OTOH,您可以为 MyData 创建一个基类,该基类具有从构造函数调用的“抽象”方法“parseData”。您可以说 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 回答