2

I am creating a module that reads CSV and Excel files using Apache POI and Opencsv.

For reading CSV files, I am creating 1 class and 2 methods:

class CsvReader {
  void open() {//implementation}
  List<CsvDto1> get1() {//implementation}
  List<CsvDto2> get2() {//implementation}
  void close() {//implementation}
}

For reading Excel files, I am also creating 1 class and 2 methods:

class ExcelReader {
  void open() {//implementation}
  List<ExlDto1> get3() {//implementation}
  List<ExlDto2> get4() {//implementation}
  void close() {//implementation}
}

All I want is to implement a pattern that will be helped maintainable in the future. So I created an interface called FileReadable:

interface FileReadable {
  void open();
  List<CsvDto1> get1()
  List<CsvDto2> get2()
  List<ExlDto1> get3()
  List<ExlDto2> get4()
  void close();
}

then CsvReader and ExcelReader will be implemented from FileRedable. The issue is get1() and get2() exist in CsvReader but they do not exist in ExcelReader, get3() and get4() exist in ExcelReader but they do not exist in CsvReader. How do I create a common read method for both classes or do we have any design pattern for this case?

   interface FileReadable {
      void open();
      Reader read();
      void close();
   }
andrewJames
  • 19,570
  • 8
  • 19
  • 51
Hola
  • 115
  • 6
  • 1
    Side note: if it has a `close()` method, you most probably want it to extend [`AutoCloseable`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/AutoCloseable.html) to support [try-with-resources](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) – Holger Nov 10 '22 at 12:52

3 Answers3

2

From your question, I see that you have Dto1 and Dto2. Prefixes Csv and Exl are part of implementation and should not be part of definition. You would need one interface called:

interface FileReadable {
  void open();
  List<Dto1> get1()
  List<Dto2> get2()
  void close();
}

And then your clients will not be aware or care if it comes from Exl or Csv. They will just use Dto1 or Dto2. If you really need to know the origin, you can create property in Dto class that can have values like EXCEL, CSV.

c3R1cGFy
  • 505
  • 4
  • 12
1

You can also use generic interface:

interface FileReadable<T> {
  void open();
  List<CsvDto1> get1();
  List<CsvDto2> get2();
  void close();
}

And just implement this interface in appropriate classes.

Read more about generic interface here in this nice post.

StepUp
  • 36,391
  • 15
  • 88
  • 148
1

Strategy pattern is the best.

i usually write like this:

public interface FileReadable {
    String getFileType();
    void open();
    Reader read();
    void close();
}

@Service
public class Csv1File implements FileReadable{
    @Override
    public String getFileType() {
        return "csv1";
    }

    @Override
    public void open() {

    }

    @Override
    public Reader read() {
        return null;
    }

    @Override
    public void close() {

    }
}

use like this

    @Resource
    List<FileReadable> fileReadable;

    String file = "csv1";
        Optional<FileReadable> first = fileReadable.stream().filter(read -> Objects.equals(file, read.getFileType())).findFirst();
        if (first.isPresent()) {
            first.get().read();
        }

you can also create like Csv2File.class Excel1File.class and use getFileType() to mark this class is support which kind of file.