Search code examples
javadesign-patternscallable

java callable with multiple methods


I`m trying to implement a java callable with multiple methods and my situation is like this:

interface FsHandler {

  boolean existDirectory();
  boolean existFile(File);
  ...
}
interface FileHandler {

  void write(byte[]);
  void read(byte[]);
  ...
}

All these methods are implemented by different storage backends like local file system, aws s3, etc

While search for a way to parallelise this code, I tried to use callable but if I understood correctly, I would need to to break my interface in many small abstract classes that implements callable - one for each method and then use callable, not to mention that I would still need to find a way to add the call to the blocking queue (which is using an executor)

So, my doubts are:

  • is it possible to wrap callable on already existing/implemented methods?
  • another idea is to create an abstract class for each method that needs to implement callable and then use the strategy pattern to decide which implementation to use

e.g.:

interface WriteStrategy implements Callable<Void> {

  Void call();
  void write(byte[]);
  static WriteStrategy localFSStrategy(){...}
  static WriteStrategy S3Strategy() {...}

class FileHandler {
  private WriteStrategy strategy;

  public FileHandler(WriteStrategy strategy) {...}
  public void write(byte[]) { strategy.call(); }
}

I`m still not sure how to approach it :'(


Solution

  • You don't even need to declare any of the classes with implements Callable.

    Instead you could use method references or lambda declarations which have the correct signature for the Callable interface with the appropriate auto-boxing for the return types. This allows one class to provide multiple Callable implementations.

    Here are some examples of different styles:

    class Impl {
        public Impl() {};
    
        public void write(byte[] bytes) {
            throw new RuntimeException("not implemented yet");
        }
        public int doSomeOp() {
            return 0;
        }
        public byte[] read() {
            throw new RuntimeException("not implemented yet");
        }
    }
    public static void main(String[] args)
    {
        Impl impl = new Impl();
    
        byte[] ba = {65, 66, 67};
    
        // Method references with different return types
        Callable<Integer> callable1 = impl::doSomeOp;
        Callable<byte[]>  callable2 = impl::read;
    
        // lambda for void method, or methods needing extra arguments
        Callable<Void>    callable3 = () -> { impl.write(ba); return null; };
    
        // Then you can use with ExecutorService:
        ExecutorService  executor = Executors.newCachedThreadPool();
        Future<Integer> task1 = executor.submit(callable1);
        
        // You don't even need to declare the local variable "callable1"
        Future<Integer> task2 = executor.submit(impl::doSomeOp);
    }