java – Architectural problem for class combination (cartesian product) for save format strategy

Hello to everyone and thank you for any suggestion.

I have a family of subclasses of Track (Track is abstract).
Every concrete Track has a different number and types of fields (attributes) that extend the basic abstract track-fields.

Then I have a family of file-types that implement some methods (saveRow() etc..).
Every file is presumed to have different type of row-formatting (imagine csv tabs, headers etc..)

ex:

  • SimpleTrack: double lat, double lon, Calendar dateTime.
  • DetectionTrack: (as SimpleTrack) + boolean detection.
  • ..
  • CsvFile
  • TxtFile

When I create a new (X)Track and a (Y) file, they are independent by nature, but.. the row is a cartesian product of track & row types.

EDIT
(to be more clear): How can I have many concrete-tracks in one hand and many FileTypes in the other hand, and create well-formatted-rows (differents for every file) by tracks which have different data (columns, headers..)?
es:

  • XtrackRow(double a, double b, Calendar date) -> to -> CVSfile (tab delimited with headers)
  • XtrackRow(double a, double b, Calendar date) -> to -> TXTfile (formatted columns and title)
  • YtrackRow(double a, string, b, int c, double e) -> to CSV ..
  • YtrackRow …. -> to -> docx file (with another kind of table or tabulation)
    ..

I see two kinds of solutions:

  1. Tracks send (right)-formatted row to file: every track has to know which kind of format apply (to rows) to give them to save to any specific type of file.
  2. Tracks send raw-data to any kind of file which is responsible to format them: in this case, the file-class must know wich kind of data has to format (every track has diffrent contents, columns, headers..). Moreover, every track-class has to send different number and types of parameter..

The second solution seems to be more fitted to the Single-Responsibility principle.. but I have no Idea how to implement it.

I tried to use Bridge Pattern to solve this problem (using first solution):

abstract class Track{
  ...
  FileInterface file;

  Track(FileInterface fileType){
    this.file = fileType;
  }

  abstract String formatConcreteTrackRow();
  
  void sendRow(){
    String rowToSave = formatConcreteTrackRow();
    file.saveRow(rowToSave);
  }
}

By this way the problem is not already solved, because every concrete-track has to implement a set of methods which returs right formatted rowString: one for every file-type.
If I use a Strategy Pattern:

class SimpleTrack extends Track{
  ...
  RowFormatStrategy rowStrategy;
  
  @override
  String formatConcreteTrackRow(){
    return this.rowStrategy.getRowString("args")
}

but in this case.. every concrete-track require a different StrategyInterface, because every concrete-track has different number and types of arguments to elaborate..
If I do not use Strategy Pattern and I define a set of methods (formatCsvRow(args),formatTxtRow(args)..) I need to include a switch(fileType) loop to choose which method to use.. breaking SOLID principles.. 🙁

Moreover..
how to impose, for every new concrete-track to have right row-format methods for every existent file-template-row?
and.. How to impose, at the same time, for every new file-class to impose new templates and relative methods in every existent concrete-track?

To be honest, it’s also quite reductive impose formatConcreteTrackRow to be a String, but it’s over and over the main problem.

I’m not interested to maintain this kind of class structure, this is only the best solution I found trying to follow SOLID principles. If you can show me a better solution, my intent is to study and understand SOLID procedures to solve these kind of purposes.

(I looked around for similar questions, but I’m not even able to define the specific problem itself..)
Thank you very much.