3

I have my input file in all XML/JSON/CSV/EXCEL formats.

I have written separate parsers for all. but I wanna choose my parse at run time.

which design pattern will be most suitable ?

I have implemented such as

if(file.endsWith(".csv"))
return CSVParser();
if(file.endsWith(".json"))
return JSONParser();
if(file.endsWith(".xml"))
return XMLParser();
else
return EXCELParser();

suggestion/guidelines please .

  • You might want to extract the extension first, i.e. text after last period, convert it to lowercase, then use a string `switch` statement, but other than that, it seem ok to me. – Andreas Jul 27 '17 at 02:25

4 Answers4

6

Simple factory would solve your problem. It's not a true design pattern (not in the original GoF set of patterns), but rather a way to code a solution (aka idiom). It's different from Factory Method and Abstract Factory (see the link above to understand the difference).

enter image description here

You don't have to use a static method. But it's practical to put it in the superclass.

Fuhrmanator
  • 11,459
  • 6
  • 62
  • 111
1

First of all, all parser implements the same interface:

public class CSVParser implements Parser {}
public class JSONParser implements Parser {}
public class XMLParser implements Parser {}
public class EXCELParser implements Parser {}

Then two solutions for reference:

1.get parser in a method:

public Parser getParser(String file){
    if(file.endsWith(".csv"))
        return CSVParser();
    if(file.endsWith(".json"))
        return JSONParser();
    if(file.endsWith(".xml"))
        return XMLParser();
    else
        return EXCELParser();
}

2. use enum:

public class ParserEnum {
    CSVParser("csv", new CSVParser()),
    JSONParser("json", new JSONParser()),
    XMLParser("xml", new XMLParser()),
    EXCELParser("excel", new EXCELParser);

    private String type;
    private Parser parser;

    ParserEnum(String type, Parser parser) {
        this.type = type;
        this.parser = parser;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Parser getParser() {
        return parser;
    }

    public void setParser(Parser parser) {
        this.parser = parser;
    }
}

then get parser from enum type:

public Parser getParser(String file){
    if(file.endsWith(".csv"))
        return ParserEnum.CSVParser.getParser;
    // ...
}
Dave Pateral
  • 1,415
  • 1
  • 14
  • 21
  • 1
    Why do you believe the parsers don't already implement a common interface? The question code has four `return new XxxParser()` statements, so unless return type is `Object`, it would be an interface or a common base class. Your answer adds nothing of value, since the enum still requires exactly the same kind of multiple `if` statements. – Andreas Jul 27 '17 at 02:28
1

You can use factory design pattern..Let subclass decide which type of parser to instantiate based on input..also client's of parser doesn't have to know which type of parser is instantiated by declaring one common interface o abstract class..for clients it will be as simple as

Parser parser=ParserFactory.getParser(String file);

parser.parse(file);

Sagar Kadu
  • 251
  • 2
  • 18
  • "Let subclasses decide" implies factory method, but that pattern requires a polymorphic call (and `getParser` is not polymorphic). You're using a [simple factory with a static method](https://stackoverflow.com/a/20859513/1168342), which is a different pattern. – Fuhrmanator Jul 29 '17 at 21:09
  • @Fuhrmanator I meant the same..the flexibility of choosing between simple factory or factory method is up to user I believe..Thanks for down vote though :) – Sagar Kadu Aug 01 '17 at 06:07
1

As the number of parsers are finite and accept same initializing parameters, Prototype pattern will fit best as follows. Note: Prototype uses cloning and does not create new objects every time. I believe we require non-shared instances.

public class ParserRepository {
    private Map<String,BaseParser> prototypes = new HashMap<>();
    public ParserRepository(){
        prototypes.put(".csv",new CSVParser());
        prototypes.put(".json",new JSONParser());
        prototypes.put(".xml",new XMLParser());
    }

    public BaseParser getParser(String extension) {
        BaseParser parser = null;;
        try {
            parser = (BaseParser) prototypes.get(extension).clone();
        } catch (CloneNotSupportedException e) {
            // Handle exception here
            e.printStackTrace();
        }
        return parser;
    }
}

In case new prototype needs to be added to the repository dynamically, appropriate methods can be exposed for the ParserRepository class.

Kedar Tokekar
  • 418
  • 3
  • 10
  • The complexity of prototype is offsetting the problem of "avoiding the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) when it is prohibitively expensive for a given application." It seems that part of the pattern doesn't apply in the OP's post. – Fuhrmanator Jul 29 '17 at 21:15