ahe
ahe

Reputation: 128

Good way of calling Functions/class in big if/else constructs

I've seen many code-parts in mostly old(java mostly) projects that look like

if(type == typeOne){
    callFunctionOne();
}else if (type == typeTwo){
   callFunctionTwo();
}else if (type == typeThree){
   callFunctionThree();
}//i've seen over ~800 lines like this!

where "type" could be an enum or really anything and the whole thing could be written in switch/case style too. My question is: is there a "better"(more stylish/shorter/more readable) way to achieve this? I've seen constructs in PHP like:

//where $type = "one","two" etc.
$functionName = 'callFunction' . $type; 
new $functionName();

But im not sure if this is realy the "better" way and if its even possible in other Languages.

Upvotes: 2

Views: 78

Answers (2)

Mshnik
Mshnik

Reputation: 7042

As noted in a comment, there are ways to use java's reflection capabilities. See this question for how to do that. That said, reflection is pretty bad style in java and should only be used if you really have no other option. Java's really big on OO-style programming and static type checking and using reflection is skimping on both of those focuses. In doing so you'll likely make your code just as complicated and way harder to debug.

Without reflection, there's not much better you can do if the code block in question happens exactly once. You'd have to implement the logic somewhere, probably involving the same if-else/switch block. However, if you find yourself copy-pasting that same if-elseif-elseif-elseif.... block in multiple places, you can do a bit better.

If type is an enum, you can move the logic to the enum itself, which is really nice from an OO standpoint. Consider the following:

public enum Direction {
    NORTH,
    SOUTH,
    EAST,
    WEST
}

public class Foo {

   public void bar(Direction d) {

       //At some point we want some logic to depend on the vector dx,dy form of d
       int dx = 0;
       int dy = 0;
       switch(d) {
           case NORTH: 
               dy = -1;
               break;
           case SOUTH: 
               dy = 1;
               break;
           case EAST: 
               dx = 1;
               break;
           case WEST: 
               dx = -1;
               break;
        }

        //Use the values in dx, dy
    }
}

It's clearly a bad idea to copy-paste this block around your project. If you ever add a new direction, you'd have to return to every such block to add the correct addition. From an OO standpoint, the dx, dy fields are truly part of the enum value, and should be a part of it to begin with. Thus we can change the above to the following:

public enum Direction {
    NORTH,
    SOUTH,
    EAST,
    WEST;

    public int getDX() {
        switch(this) {
            case WEST: return -1;
            case EAST: return 1;
            default: return 0;
        }
    }

    public int getDY() {
        switch(this) {
            case NORTH: return -1;
            case SOUTH: return 1;
            default: return 0;
        }
    }
}

Or, (IMO) even better, represent them as a field

public enum Direction {
  NORTH(0,-1),
  SOUTH(0,1),
  EAST(1,0),
  WEST(-1,0);

  private int dx;
  private int dy;

  private Direction(int dx, int dy) {
    this.dx = dx;
    this.dy = dy;
  }

  public int getDX() {
    return dx;
  }

  public int getDY() {
    return dy;
  }
}

From there we can simply use these methods directly in Foo.bar() and don't need any logic:

public class Foo {

   public void bar(Direction d) {

       //can directly use d.getDX() and d.getDY()

   }

}

Your question about function calling is the same, if one level removed. We can either add the switch straight to the enum:

public enum Type {
    VALUE_ONE, VALUE_TWO, ...

    public void callFunc() {
        switch(this) {
            case VALUE_ONE:
                callFunctionOne();
                return;
             case VALUE_TWO:
                callFunctionTwo();
                return;
             //....
        }
    }
}

And then just use it by directly referencing that function:

Type t = //....
t.callFunc();

You could even use some java-8 stuff to represent the function-to-call as a field

@FunctionalInterface
public interface Unit {
    public void apply();
}

public enum Type {
    VALUE_ONE(Foo::baz),
    VALUE_TWO(Foo::baz2),
    //...

    private Unit funcToCall;

    private Type(Unit u) {
        this.funcToCall = u;
    }

    public void callFunc() {
        funcToCall.apply();
    }
}        

If type is not an enum, you can use some (but not all) of the above options. You can still lump your switch logic into a helper method/class and pass control over to it instead of copy/pasting. The more that type is supposed to represent something, and the more you find yourself wanting to switch over it, the more likely an enum is the correct choice.

Upvotes: 1

Abaddon666
Abaddon666

Reputation: 1623

The more interesting question imo is what exactly you want to achieve by this?

Java is an object-oriented language. Therefore i would solve this by one subclass per type:

abstract class Type{
     abstract void method();
}

class Type1 extends Type{
     void method(){
         //do sth. specific for this type
     }
}

If the methods are actually all in the same class you could still call them out of these classes by simply passing yourself (i see that this could get ugly).

class RandomClass(){
     void method1(){
         //do sth for type1
     }
     void method2(){
         //do sth for type2
     }
}

abstract class Type{
     RandomClass randomClass;
     Type(RandomClass randomClass){
         this.randomClass = randomClass;
     }
     abstract void method();
}

class Type1 extends Type{
     void method(){
         randomClass.method1();
     }
}
class Type2 extends Type{
     void method(){
         randomClass.method2();
     }
}

Otherwise you could use reflection, like suggested by Sohaib (example taken from his suggested link):

Yyyy.class.getMethod("methodName").invoke(someArgs)

But using Reflection for somehting like this seems very unhandy as it is inperformant and a nice trap for later maintenance (just imagine someone starts renaming the methods).

So to answer the question itself (at least how i understand it):

Dynamically calling methods e.g. by determining their name dynamically at runtime, is something you only do in scripting languages. The object-oriented approach might come with overhead, but at the end is the better style for this kind of language.

If both solutions do not work for you, a switch statement or if-else cascade is your best alternative.

Upvotes: 1

Related Questions