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\$
4
  • \$\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\$ Commented Jun 24, 2013 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\$
    – TtT23
    Commented Jun 25, 2013 at 1:28
  • \$\begingroup\$ Why are you getting the device temperature, screen brightness, etc., but not returning it? \$\endgroup\$ Commented Jun 25, 2013 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\$ Commented Jul 25, 2016 at 17:34

3 Answers 3

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\$
12
  • 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
    Commented Jun 18, 2013 at 8:20
  • \$\begingroup\$ @fge Good spot, renamed. \$\endgroup\$ Commented Jun 18, 2013 at 8:24
  • \$\begingroup\$ You can also use Runnable. \$\endgroup\$
    – tcb
    Commented Jun 18, 2013 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\$ Commented Jun 18, 2013 at 20:20
  • \$\begingroup\$ or use reflection? \$\endgroup\$
    – Ven
    Commented Jun 19, 2013 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\$
4
  • \$\begingroup\$ Sounds like a good idea. Can you give me an example of a framework that does such job for me in Java? \$\endgroup\$
    – TtT23
    Commented Jun 18, 2013 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
    Commented Jun 18, 2013 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
    Commented Jun 18, 2013 at 8:19
  • \$\begingroup\$ There is another solution too, see my edit \$\endgroup\$
    – fge
    Commented Jun 18, 2013 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 and acknowledge you have read our privacy policy.

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