Вопрос

I am currently working on project which requires parsing different types of files. The content of the files will populate the classes.

eg: file1 populate content of class1,  file2 populate content of class2, etc.

My question is where should the parsing logic of the file go?

1st option : If I place the parsing logic inside the class then the class becomes really huge, because currently the class itself is ~400 lines and will grow in the future. The advantage of this way is things are really simple, I could use operator>>, etc.

2nd option : Create a new class that takes std::istream and class reference, do the parsing logic there and populate the class. The advantage of this way of doing is that if the class remains the same but formatting of the file changes,

eg: `file1 populate content of class1 or file1-v2 populate content of class1`

I have to use polymorphism to change the parsing logic and keep the design simple. Disadvantage of this option is, it's a little complex but I can live with it.

Which option do you think is better or is there any other better option?

Это было полезно?

Решение

Classes should have a single responsibility. Presumably the responsibility of parsing a file to populate a class and the responsibilities of class itself are distinct and should not be combined. Therefore the 2nd option is the better design.

It's pretty common for file formats to change as a system develops, and by separating this concern now, you'll be in a good place if the file format or the class you're populating changes. It's also a design that allows greater flexibility--e.g. if you decide you want to populate your class from the network, a different file format, etc.

Другие советы

If the parsing is more complex than a few lines of code, it is probably better to put it into separate classes, lets call them Class1Parser, Class2Parser and so on. If those classes contain similar parts, you can either

  • create a common base class BaseParser for both where the reusable parts live

  • create a separate utility class ParserUtil for reusable functions

  • make the parser a generic class like Parser<T> where T is either Class1 or Class2

  • use a combination of the former options (like making only the ParserUtil or BaseParser generic)

Note that an inheritance hierarchy of the parsers now can be independent from the inheritance hierarchy of the parsed classes, this would not quite be possible by putting the parsing logic directly into Class1/Class2.

None of these options should hold you back from implementing an operator>> for Class1 or Class2, if you think you need it for ease of use. This operator can simply delegate its functionality to an object of type Class1Parser or Class2Parser.

If these approaches end up becoming more complex than putting the parser logic into Class1 or Class2, then there is something utterly wrong in your code. In essence, it should actually be the same logic. By putting the parsing logic into separate classes, it stays decoupled from other methods in those classes, which should reduce the overall complexity, not increase it.

Лицензировано под: CC-BY-SA с атрибуция
scroll top