0

For a project we have a requirement to create an interfacedefinition that will return all available filetype extensions that our component can export...

The problem is that we want avoid configuration/properties files. We don't want to edit our configuration/propertie file when another filetype is added (in the future). The structure of this part of our component is as follows:

public abstract class FileType {
    protected String filetype;

    public FileType(String filetype){
        this.filetype = filetype;
    }

    public abstract void export(String path, Object information);
}


public class PdfExport extends FileType {
    public PdfExport() {
        super("pdf");
    }

    public void export(String path, Object information){
        //pdf specific logic
    }
}

But how do we solve this when another component calls the interfacedefinition getExportTypes()? (How do we get a list of all available filetypes?) Taking into account the requirement to add in the future new classes that extend abstract class filetype (add new filetypes)?

Does anyone has suggestions, maybe another structure of above example? Or any (design) that discuss above issue?

Thanks in advance!

QuantumMechanic
  • 13,795
  • 4
  • 45
  • 66
mrtentje
  • 1,402
  • 2
  • 22
  • 43
  • 2
    I would suggest to use enum. Then you will the list of available types just by adding a new item to the enum. – khachik Mar 25 '12 at 18:18
  • 1
    the design is clear, just store all your exporters in List and return it when you call getExportType(). and yes, you need to add your new type to this list when you make new expoter. – Wajdy Essam Mar 25 '12 at 18:35

3 Answers3

3

You could do something like this:

public interface FileType {
    public String getFileType();
    public void export(String path, Object info);
}

public enum DefaultFileType implements FileType {
    PDF(".pdf"){
        public void export(String path, Object info) {
            // do pdf stuff
        }
    }, TXT(".txt"){
        public void export(String path, Object info) {
            //do txt stuff
        }
    };

    private final String fileType;

    private DefaultFileType(String fileType) {
        this.fileType = fileType;
    }

    public String getFileType() {
        return fileType;
    }

    public abstract void export(String path, Object info);
}

Then you can have a Set<FileType> in your class of all the supported FileTypes. This way anyone who wants to add a supported FileType but cannot edit your enum can still do so.

Jeffrey
  • 44,417
  • 8
  • 90
  • 141
2

This is the exact purpose of the strategy pattern. The strategies here are the FileTypes that encapsulate an algorithm that exports a file. In the following example:

public class Application{
 List<FileType> exporters = new ArrayList<FileType>();
 public void addExporter(FileType fileExporter){
  exporters.add(fileExporter);
 }
 public void exportData(Object information){
   for(FileType exporter : exporters){
    exporter.export("d:\Export", information);
   }
 }
}

The Application class holds a list of exporters that can be filled out on the go. The Application class does not have to know what type of file exporter is registered nor how the file can be exported. When the data is exported, the Applicaiton class loops through registered exporters and delegates the export task to each one of them.

EDIT Below is an example of the Application class usage.

// Define a pdf exporter
PdfExport pdfExport = new pdfExport();
Application app = new Application();
// Register the new exporter
app.addExporter(pdfExport);
// Export some data...
app.export(information);

EDIT How to avoid configuration files and changing the code everytime you have a new FileType? You can load the exporters at runtime using reflexion (see this link for details)

GETah
  • 20,922
  • 7
  • 61
  • 103
  • what class is responsible for registering the exporters? – jtahlborn Mar 25 '12 at 18:22
  • @jtahlborn in the example I gave in the post it is the `Application` class. This can be replaced by whatever class you want :) – GETah Mar 25 '12 at 18:29
  • yes, i understand the concept. my point is that some code still has to be responsible for registering all the types. how is this different from the configuration file the OP wanted to avoid? – jtahlborn Mar 25 '12 at 18:35
  • @jtahlborn Yes but that code does not have to change. The OP can load the classes using reflection for example and add the new exporters at runtime – GETah Mar 25 '12 at 18:39
  • How would the OP use reflection to load the classes? (not sure if you realize, but you can't find all the classes of a specific type using reflection, you need a secondary mechanism). – jtahlborn Mar 25 '12 at 18:41
  • like i said, it's not about reflection, you need a secondary mechanism (e.g. the code which scans the assemblies/jar files for classes which extend a certain interface). @Vadym's answer actually links to ways to do this in java. – jtahlborn Mar 25 '12 at 18:48
  • @GETah thanks for your solution. I will look further for a secondary mechanism as jtahlborn mentoined. Or do you have any suggestions/tips for this? – mrtentje Mar 25 '12 at 18:57
1

You can use reflection to scan classes which implement your interface.

Have a look at similar question: At runtime, find all classes in a Java application that extend a base class

Community
  • 1
  • 1
Vadym S. Khondar
  • 1,428
  • 2
  • 12
  • 19
  • you link to a good solution. The ServiceLoader mechanism is mostly just a well-defined configuration file (less about reflection, more about config file). i guess it's up to the OP whether or not that fits the definition of "not editing a configuration file". – jtahlborn Mar 25 '12 at 18:41