Setup
Setup

Reputation: 330

Is there a way to assign the Class in Java at Runtime?

Since I am refactoring my code right now, I am trying to first sort out possible code duplicates. As a posible duplicate I see the 2 methods which basically read files: One from FTP and another from local filestructures. The problem is, that they of course use different classes to achieve the same things. The variable directoryListing is the tricky one.

readLogs(boolean useFTP){
// directory Listing of course needs to be of a different type here or not being declared at all 
    File[] directoryListing;
    File dir = Settings.getInputfolder();
    if(useFTP){
            // ftp implementation
            client = new FTPClient();               
            client.connect(Settings.getFtpHost());
            client.enterLocalPassiveMode();
            client.login(Settings.getFtpLoginname(), Settings.getFtpPassword());
            client.setFileType(FTPClient.BINARY_FILE_TYPE);

            //directoryListing should be of type FTPFile[] here
            directoryListing = client.listFiles(Settings.getInputfolder().toString());
    else{
        //directoryListing should be of type File[] in this case
        directoryListing = dir.listFiles();
    }
    if (directoryListing == null) {
            log.error("Input-folder not found:" + dir.getAbsolutePath());
    }
    //I want to be able to iterate - here myFolder might be of type File oder FTPFile
    for (Object myFolder : directoryListing) {
    ...
    ...
    }
}

The only methods I am using later on in the code have the exact same signatures on both File and FTPFile:
getName()
isFile()
isDirectory()
listFiles()
I have used reflections for those e.g.

Method getFolderNameMethod = myFolder.getClass().getMethod("getName");
String name = (String) getFolderNameMethod.invoke(myFolder);

In what way can I achieve a somewhat dynamic declaration of the directoryListing variable?

Thank you very much for your time and advice in advance! :D

Upvotes: 0

Views: 81

Answers (2)

CoronA
CoronA

Reputation: 8075

Use Strategy and Adapter pattern:

First define the common interface

interface VirtualFile {
  /*
  getName()
  isFile()
  isDirectory()
  listFiles()
  */
}

then create two adapter (one for File, one for FTPFile)

class FileAdapter implements VirtualFile {
  // delegates every method to the wrapped file
}

class FTPFileAdapter implements VirtualFile {
  // delegates every method to the wrapped ftp file
}

Then refactor readLogs:

readLogs(FileStrategy fileOrFtp){
  List<VirtualFile> directoryListing = fileOrFtp.listDirectory();
  for (VirtualFile myFolder : directoryListing) {
    ...
    ...
  }
}

interface FileStrategy {
  List<VirtualFile> listDirectory();
}

class FTPFileStrategy implements FileStrategy {
   // implement useFTP case (settings should be passed to the constructor
   // wrap each resulting FTPFile into a FTPFileAdapter
}

class LocalFileStrategy implements FileStrategy {
   // implement !useFTP case (settings, e.g. root folder should be passed to the constructor
   // wrap each resulting File into a FileAdapter
}

This keeps all concerns separated and additionally: - no ifs any more - no casting - no reflection - shorter methods/classes

Upvotes: 1

mikeb
mikeb

Reputation: 11267

I have used apache-commons-vfs in the past:

http://commons.apache.org/proper/commons-vfs/

If you don't want to ad a lib, you could create an interface and 2 implementations of it, but VFS will give you more supported filesystems and probably be easier to maintain in the long run.

I'm sure there are others, but I've never needed anything other than the apache one so I can't speak to them.

Edit:

Based on your comment, I'd crate a factory, something like:

public class FSFactory{
  public static IFSInteface getInterfaceFor(FSType t){
     if(FSType.FTP.equals(t)){
        return new FtpFileGetter();
     } else if (FSType.LOCAL.equals(t)){
        return new FSFileGetter();
     }
  }
}

The code is not tested, but this is how I handle stuff like this.

Upvotes: 1

Related Questions