-1

Currently I have a class hierarchy for parsers with Parser as interface with CSVParser and XMLParser classes implementing it. The interface has following methods :

List<AType>parse(File file)

Now I want to add another parsing method that would take the same File type as the argument, but will return List<BType>. This method is required only for CSV type of files and not the for XML type files.
One simple option is to add a new method to the Parser interface with a different name. Also provide empty implementation in XMLParser file and the required method implementation in CSVParser. But some how I don't feel this is the right choice. I want to know what would be a better alternative choice?

  • And if you had a JSonParser, would you have both parsing methods or only the current one? – Christophe Jun 23 '17 at 7:40
  • Another related question: how does the consuming class know that it's a CSVparser and that it sould use the second method? – Christophe Jun 23 '17 at 7:47
  • Consuming class gets the parser instance from a factory class. The factory creates a parser instance based on the file extension that is being parsed – Innocuous Jun 23 '17 at 8:12
  • "This method is - currently(?) - required only for CSV type of files"? But it could make sense, at least in principle (maybe in the future), for XML files? Then there is nothing wrong by providing an empty implementation in XMLParser ("empty" in the sense it throws a "NotImplementedException" exception), feels quite right for me. – Doc Brown Jun 23 '17 at 9:23
  • 2
    @Innocuous: If the customer next asks to support the new method also for XML files, would you consider that request reasonable or absolutely crazy? – Bart van Ingen Schenau Jun 23 '17 at 10:23
1

This a perfect example for generics combined with the Template-Method pattern (Template Method Pattern):

public interface Parser<T> {
    List<T> parse(File file);
}

public abstract class CSVParser<T> implements Parser<T> {
    public List<T> parse(File file) {
        List<String[]> lines = loadCSV(file);
        return buildResult(lines)
    }
    public abstract List<T> buildResult(List<String[]> lines);
}

public class CSVParserAType extends CSVParser<AType> {
    public List<AType> buildResult(List<String[]> lines) {
        ...
    }
}

public class CSVParserBType extends CSVParser<BType> {
    public List<BType> buildResult(List<String[]> lines) {
        ...
    }
}

public class XMLParser implements Parser<AType> {
    public List<AType> parse(File file) {
        ...
    }
}

The generic type is used to keep the result type flexible to future implementations.

The Template-Method pattern is used to reuse csv-loading mechanism and ensure that it is done before building the result.

List of String[] contains the lines of the CSV-File separated in columns (String[], String-Array). If your CSV is huge, you may consider something like a cursor or a stream so you can abstract from the way how much data is actually read into memory to have a better memory footprint.

3

If I understand you correctly, you wish to add a second parser method in CSVParser that returns a different type? Is this correct? Then I'm assuming both of these methods would parse CSV files?

The problem derives from naming conflicts as I see it. The way you structured your code, there is one Parser for each type of file, in this case an XMLParser for xml files and a CSVParser for csv files. This is all fine and good if you will only have one said parser for each type of file, but apparently that is no longer your case.

While you could add a 2nd method in Parser and throw an NotImplementedException in the XmlParser second method, you have to really ask yourself if XmlParser is the exception to the rule or if it is the rule. In other words, if tomorrow you decide to add yet another parser, will this new parser return both a list of AType as well as a list of BType? In that case, XmlParser is the odd one out, and it may be a good idea to extend this behavior to Parser, but I'm guessing that isn't the case here.

Therefore it would most likely be a bad idea to add a 2nd method to Parser. Thankfully there are alternatives! Since a Parser implementation is no longer tied only to the type of file, a straightforward solution might be to have a CSVToATypeParser and a CSVToBTypeParser. In this way, you can use either one as you would any other parser.

A second solution would be to combine AType and BType somehow as CType so that a call to CSVParser would return all the information you'd require from AType and BType in a single method. Consider that you're still performing one pass regardless, so aside from a increase in memory usage, there would be no added disadvantage.

Hope that helps!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.