0

I am trying to use RabbitMQ and based on different message, different implements should be called.

I set the message format as of JSON, and there is a field "callType", the value of it is the class name implements a common interface. e.g, all implementations have implements interface "Task", and I have implementation of "TaskImp1","TaskImp2","TaskImp3".

So the code should be like

if (callType=="TaskImp1")
((Task)TaskImp1).runTask()
if (callType=="TaskImp2")
((Task)TaskImp2).runTask()
if (callType=="TaskImp3")
((Task)TaskImp3).runTask()

But could it be more flexible? If later I develop a new one "TaskImp4", I don't want to change the calling code, is it possible to have java automatically pick the right implementation since the callType is actually the class name of the implementation.

ha9u63a7
  • 6,233
  • 16
  • 73
  • 108
Daniel Wu
  • 5,853
  • 12
  • 42
  • 93

4 Answers4

1

Yes, for example, through Java reflection (What is reflection and why is it useful?). Reflection has a performance cost though (Java Reflection Performance)

Community
  • 1
  • 1
ovdsrn
  • 793
  • 1
  • 7
  • 24
0

Sure: put your Task instances in a map:

private Map<String, Task> tasksByName = new HashMap<>();

...

tasksByName.put("TaskImp1", new TaskImp1());
tasksByName.put("TaskImp2", new TaskImp2());
tasksByName.put("TaskImp3", new TaskImp3());

...

String callType = message.getCallType();
Task task = tasksByName.get(callType);
task.runTask();

Also, read How do I compare strings in Java?

Community
  • 1
  • 1
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
0

You have an opportunity to use Strategy here. So for e.g. you could do like:

 public class MyTask {
     private Task task;
     public MyTask(Task task) {
         this.task = task;
     }

     public void doSomething() {
         task.runTask();
     }

     public static void main(String args[]) {
         MyTask task = new MyTask(new TaskImpl1());//or even you could use setTask() api to inject task at runtime rather than doing cast on compile time.
         task.doSomething();
         task = new MyTask(new TaskImpl2());
         task.doSomething();
         task = new MyTask(new TaskImpl3());
         task.doSomething();
     }
 }

In this way you could make your code extensible. Tomorrow if you have taskImpl4, you could code it independently and inject in MyTask without even touching MyTask class implementation.

SMA
  • 36,381
  • 8
  • 49
  • 73
0

As @ovdsrn already said you can use reflection. Simple example would be something like (the key is getTask static method. Also, note that, when you are using Class.forName you must specify whole "path" (package) for your class)

// ITask.java

package main;

public interface ITask {
    void doSomething();
}

// Task1.java

    package main;

    public class Task1 implements ITask {
        @Override
        public void doSomething() {
            System.out.println("Task1");
        }       
    }

// Task2.java

    package main;

    public class Task2 implements ITask {
        @Override
        public void doSomething() {
            System.out.println("Task2");
        }       
    }

// main

package main;

public class JavaTest {
    private static ITask getTask(String name) {
        try {
            Class<?> cls = Class.forName(name);
            Object clsInstance = (Object) cls.newInstance();
            return (ITask)clsInstance;
        } catch (Exception e) { // you can handle here only specific exceptions 
            return null;
        }
    }

    public static void main(String[] args) {
        String name = args.length > 0 ? args[0] : "Task2";
        ITask task = getTask("main." + name);
        if (task != null) {
            task.doSomething();
        }
        else {
            System.out.println("can not make instance of class: " + name);
        }
    }
}
Igor
  • 1,835
  • 17
  • 15