8
\$\begingroup\$

I'm receiving a certain string value over the network and I need to call a function based on that value.

So this is more or less what I have at the moment:

String methodName = soapObj.getMethodName();

switch(methodName) {
    case "getTemperature":
        getDeviceTemp();
        break;
    case "getBrightness":
        getScreenBrightness();
        break;
    ...
}

And.. there are about 60 to 70 methods supported like that.

What's the most elegant way of handling above situation without using reflection? (The app is moderately constrained performance-wise).

\$\endgroup\$
  • \$\begingroup\$ Do you mind me asking for more details about your use case or the domain? Just curious to know more about the situation. \$\endgroup\$ – David John Smith Jun 24 '13 at 22:47
  • \$\begingroup\$ @DavidJohnSmith Communication with a device known as Direct Digital Control that runs on embedded linux. (Building Management Solution, controlling lights, electricity, water valves etc). \$\endgroup\$ – l46kok Jun 25 '13 at 1:28
  • \$\begingroup\$ Why are you getting the device temperature, screen brightness, etc., but not returning it? \$\endgroup\$ – raptortech97 Jun 25 '13 at 19:43
  • \$\begingroup\$ My impression is that SOAP is not a lightweight, high-performance protocol to begin with. Reflection might not be such a bad choice. \$\endgroup\$ – 200_success Jul 25 '16 at 17:34
18
\$\begingroup\$

I would suggest to:

  • Define an interface with a method that will be called inside every case, e.g.

    public interface ServiceMethod {
        public void execute();
    }
    
  • Create a map that will contain contain instances of ServiceMethod findable by the a String key, e.g.:

    Map<String, ServiceMethod> serviceMethodMap = new HashMap<>();
    
  • Fill up the serviceMethodMap map with ServiceMethod instances (this is to avoid the switch), e.g.:

    serviceMethodMap.put("getTemperature", new ServiceMethod() {
        public void execute() {
            getDeviceTemp();
        });
    serviceMethodMap.put("getBrightness", new ServiceMethod() {
        public void execute() {
            getBrightness();
        });   
    
  • Finally replace the switch with:

    final ServiceMethod serviceMethod = serviceMethodMap.get(methodName);
    if(serviceMethod != null) {
        serviceMethod.execute();
    }
    

Update 2/18/2015, added Java 8 support

  • Define an interface with a method that will be called inside every case, e.g.

    @FunctionalInterface
    public interface ServiceMethod {
        public void execute();
    }
    
  • Create a map that will contain contain instances of ServiceMethod findable by the a String key, e.g.:

    Map<String, ServiceMethod> serviceMethodMap = new HashMap<>();
    
  • Fill up the serviceMethodMap map with ServiceMethod instances (this is to avoid the switch), e.g.:

    serviceMethodMap.put("getTemperature", () -> getDeviceTemp());
    serviceMethodMap.put("getBrightness", () -> getBrightness());
    
  • Finally replace the switch with:

    final ServiceMethod serviceMethod = serviceMethodMap.get(methodName);
    if(serviceMethod != null) {
        serviceMethod.execute();
    }
    
\$\endgroup\$
  • 1
    \$\begingroup\$ Uh, there is java.util.concurrent.Executor... Naming your interface like a JDK-provided class is not a very good idea ;) \$\endgroup\$ – fge Jun 18 '13 at 8:20
  • \$\begingroup\$ @fge Good spot, renamed. \$\endgroup\$ – Adam Siemion Jun 18 '13 at 8:24
  • \$\begingroup\$ You can also use Runnable. \$\endgroup\$ – tcb Jun 18 '13 at 17:18
  • 1
    \$\begingroup\$ @tcb Yes, you are right, I have suggested a new interface to allow having a different return type then void and allowing to have arguments passed to the execute method. \$\endgroup\$ – Adam Siemion Jun 18 '13 at 20:20
  • \$\begingroup\$ or use reflection? \$\endgroup\$ – Ven Jun 19 '13 at 11:58
6
\$\begingroup\$

The easiest way is to create an interface for your function, for instance:

public interface ParameterGetter
{
    int getValue()
}

Then have a Map<String, ParameterGetter> in which you would pair keys with implementations of ParameterGetter. If no entry exists, of course, the method call is wrong.

While this is easy, however, it is not practical. Many frameworks, including light ones, include annotation systems which will do the job automatically for you -- you should try and find one, and use it.

There is also another solution -- since this is JDK 7 you are using, you'll have it: ServiceLoader.

It also requires that you implement an interface, but using this, you can load your method implementations easily. This is what I use in one of my projects, and it works quite well; the only trouble with it is you need to create a file in META-INF/services, but it's a trouble you only have to do once for each method you create; or if you use Maven, there is a plugin to generate it for you.

\$\endgroup\$
  • \$\begingroup\$ Sounds like a good idea. Can you give me an example of a framework that does such job for me in Java? \$\endgroup\$ – l46kok Jun 18 '13 at 8:08
  • \$\begingroup\$ The "lightest" one I know of is reflections, but I would be surprised if there didn't exist something even lighter. Using reflections require that you write some code to handle that... But as you seem to be doing SOAP (which I have never done), there probably exists much better solutions \$\endgroup\$ – fge Jun 18 '13 at 8:14
  • \$\begingroup\$ BTW, an example of such a standardized framework (but not SOAP dedicated) is JAX-RS, an implementation of which is Jersey \$\endgroup\$ – fge Jun 18 '13 at 8:19
  • \$\begingroup\$ There is another solution too, see my edit \$\endgroup\$ – fge Jun 18 '13 at 8:27
1
\$\begingroup\$

Why don't you use reflection (I know the drawbacks) but there is trade of, rename all your methods to exact same as the service would return String and execute the method with 2 lines of code.

Method m = Class.forName(MyClass.class.getName()).getDeclaredMethod(methodName);
m.invoke(null);
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.