在我的情况下避免魔术数字的最佳方法

时间:2012-10-06 15:04:15

标签: java architecture

我正在阅读一个包含许多列(22)的文件,而我正在使用openCSV来读取文件。

每一行都表示为字符串数组nextLine[]

我必须处理/验证列,并且不希望将它们称为数字(即nextLine[0] ... nextLine[22]

我更愿意将它们称为nextLine[COLUMN_A] nextLine[COLUMN_B] ..etc.

我最初的方法是使用枚举

创建单例
public enum Columns {
    INSTANCE;
    public int COLUMN_A = 0;  
    ....
    public int COLUMN_X = 22; 
}

然后我可以将数组称为:

nextLine[Columns.INSTANCE.COLUMN_A]

问题

这是最好的方法吗? 我只是怀疑,因为我有另一个模型类,它只是为所有列都有getter / setter,现在创建另一个类(singleton)来表示列作为索引似乎是一个额外的努力。

对于上面的示例,如果我有一个类似

的模型类
public class Columns {
  private String columnA;
  public Columns (String columnA) {
    this.columnA = columnA;
  } 
  public void setColumnA(String columnA) {
    this.columnA = columnA;
  }
  public String getColumnA() {
    return this.columnA;
  }
} 

我可以以某种方式使用nextLine[columnA]而不是创建单例枚举类吗?

4 个答案:

答案 0 :(得分:2)

枚举不是单身。单例是一种特定的软件设计模式,枚举不是传统上认为的那样。你对Enum的使用至少可以说是奇怪的。如果要使用枚举来引用特定列,只需执行以下操作:

public enum Column {
    public int index;

    A(0), B(1), C(2), D(3);

    public Column( int index ) {
        this.index = index;
    }
}

然后你就说:

String columnAValue = csv[row][Column.A.index]

这样做也允许您迭代所有列,如下所示:

for( Column column : Column.values ) {
    String column = csv[row][column.index];
}

使用你使用的Enum不值得的模式你将无法做到这一点。如果你要继续做你正在做的事情,只需在文件顶部制作常规常量:

public class CsvParser {
    public static final int COLUMN_A = 0;
    public static final int COLUMN_B = 1;
    public static final int COLUMN_C = 2;

}

除了它更直接且不涉及定义另一个枚举之外,它与你使用的枚举方法没有区别。

现在回答你的问题。这是最好的模式吗?以及它所依赖的所有架构类型问题。您是否正在构建一个必须对CSV的每一列进行特定验证的程序?也许列A是一个整数,必须用作一个整数,列B是一个字符串,列C是一个枚举,等等。或者,你必须将特定的逻辑附加到每一列然后是这种模式是有意义的格式您的数据始终是可预测的。如果你必须支持多种格式的数据,但它们是固定的(即只有format1,format2,format3),那么你可以继续遵循这种模式。

但是,如果你必须阅读任何类型的csv格式,但是附加一些固定数量的解析和/或逻辑,那么你必须读入一些关于你的csv的元数据,以了解哪些列是数字,哪些是通过查看元数据可以附加您的逻辑。当然,这更灵活,但定义元数据和读取并处理它并非易事。如果您不需要此型号,请不要这样做。另一种模式的工作要少得多,而且非常强大。

如果你从大局看这个。在第一个架构中,我们拥有元数据。它是Enum或我们在程序中创建的常量。因此元数据已内置到程序中。在第二种风格中,我们将元数据从程序中移出到外部表示中,因此它不会被烘焙到程序中。用户可以在运行时更改元数据,在第一个版本中,元数据无法更改。

答案 1 :(得分:1)

有22列,我希望你不要单独处理列,而是以原理图的方式处理。因此,我会完全避免索引并做

for (String columnElement : nextLine) {
  // process columnElement
}

如果每列具有单独的特定含义,则数组(或列表或映射)也不是最佳设计。然后我宁愿选择每一行(or use a suitable framework instead of reinventing the wheel)建模的枚举或类。我不会为每一行编写(g,s)etters,而是使用多态,以便enum / class知道如何处理每一列。甚至更好:拥有一个类,用于将处理委托给Column类型的对象,例如

class Line {
  List<Column> columns;

  public void processLine() {
    for (Column c: columns) {
      c.processColumn();
  }

}

class Column {
  public void processColumn() {
    ...
  }
}

答案 2 :(得分:0)

我建议使用HashMap

在您的情况下可以是HashMap<String, String[]> map;

然后你可以得到你的价值观:

String[] valuesIAmInterested =  map.get(columnA);

更好的是,您可以真正使用对象Column作为地图的关键。

HashMap<Column, String[]>   map;

答案 3 :(得分:0)

在思考了一些后,我认为这就是你能做的事情:

public class CsvParser {

    private File file;
    private boolean hasHeader = false;
    private Map<String,Integer> columnIndexes;
    private List<String[]> rows = new ArrayList<String[]>();

    public CsvParser( File file, boolean hasHeader ) throws IOException {
        this.file = file;
        this.hasHeader = hasHeader;
    }

    // use this to parse the header from the file
    private void parseColumns(LineNumberReader reader) {
        String line = reader.nextLine();
        if( line != null ) {
            String[] columns = line.split(",");
            setColumns( columns );
        }
    }

    // use this if there is no header in the data.
    public void setColumns( String[] columns ) {
        columnIndex = new HashMap<String,Integer>();
        for( int i = 0; i < columns.length; i++ ) columnIndexes.put( columns.trim(), i);
    }

    public void parse() throws IOException {
        LineNumberReader reader = new LineNumberReader( new FileReader( file ) );
        try {
           if( hasHeader ) parseColumns(reader);
           while( (line = reader.nextLine()) != null ) {
              rows.add( line.split(",") );
           }
        } finally {
           reader.close();
        }
    }

    public Collection<String> getColumns() {
       return columnIndexes.keys();
    }

    public int size() {
       return rows.size();
    }

    public int getInt( int row, String column ) {
       return Integer.parseInt(getString(row,column));
    }

    public String getString( int row, String column ) {
       return rows.get(row)[columnIndexes.get(column)];
    }

    public double getDouble( int row, String column ) {
       return Double.parseDouble(getString(row,column));
    }

    public float getFloat( int row, String column ) {
       return Float.parseFloat(getString(row,column));
    }
}