if-else和switch语句的替代方案

15

我有以下 Java 代码:

public void doSomething(int i) {
    if (i == 12) {
        // order should be same
        up();
        left();
        stop();
    }
    if (i == 304) {
        // order should be same
        right();
        up();
        stop();
    }
    if (i == 962) {
        // order should be same
        down();
        left();
        up();
        stop();
    }
}
// similar code can be done using switch case statements.
// all the function can have any functionality and might not resemble to the name given to them.

如果禁止使用if-else和switch case语句,那么该怎么办呢?代码可以使用Java或JavaScript编写。


你确定你实际上可以使用JS(这种语言)吗?如果是的话,那很容易。 - Qantas 94 Heavy
使用字典,每个键都有一个函数列表作为值。根据所选的键,在列表中选择函数并迭代列表并调用每个函数。 - Puru--
我认为这是不可能的,如果这个数字背后有逻辑,那么可能是可以的。但如果它只是一个数字,那么你必须使用一些条件语句来识别这个数字。 - Dileep
7
我永远不会理解教育上要求学生“做X,但不准使用Y——明显正确的方法,或者Z——另一种有效但笨拙的解法”的原因。 - Nick T
5
“in either Java or JavaScript” 没有意义。它们是两种非常不同的语言,拥有完全不同的领域。为了回答你的问题,这两种语言都提供了几个多路分支结构:if-then-elseif-else 和某种形式的 switch。你当前的实现有缺陷,因为它会命中每个 if 测试,而不是在第一个匹配后退出。那可能就是讲师所说的内容。 - Phil Perry
显示剩余4条评论
10个回答

30

如果您会使用JavaScript,您可以使用一个带有函数的对象:

function doSomething(i) {
  var obj = {};

  obj[12] = function () {
    // order should be same
    up();
    left();
    stop();
  };
  obj[304] = function () {
    // order should be same
    right();
    up();
    stop();
  };
  obj[962] = function () {
    // order should be same
    down();
    left();
    up();
    stop();
  };

  // apparently we can't use any conditional statements
  try {
    obj[i]();
  } catch (e) {}
}

如果只允许使用逻辑运算符 &&,而不允许使用 ifswitch 语句,请将所有的 if 语句替换为逻辑 AND 运算符:

function doSomething(i) {
  (i == 12) && (
    // order should be same
    up(),
    left(),
    stop()
  );

  (i == 304) && (
    // order should be same
    right(),
    up(),
    stop()
  );

  (i == 962) && (
    // order should be same
    down(),
    left(),
    up(),
    stop()
  );
}

第二个使用了if语句,但第一个值得点赞;-) - Dileep
如果OP禁止使用条件语句,那就不可接受了 :) - Mohsen Kamrani
只想指出第二个建议仅适用于使用短路评估的语言:http://en.wikipedia.org/wiki/Short-circuit_evaluation。就我所知,只有Pascal不使用它。 - Hoffmann
@Hoffmann,但是OP在谈论Java和JS,它们都使用短路。 - Brian S
2
与其使用try/catch,我个人认为这种写法更易读:obj[i] && obj[i](); - Izkata

17

以下是在JavaScript中实现此操作的简单方法:

function up()    { console.log("up");    }
function down()  { console.log("down");  }
function left()  { console.log("left");  }
function right() { console.log("right"); }
function stop()  { console.log("stop");  }

var fnmaps = {
    12:  [up, left, stop],
    304: [right, up, stop],
    962: [down, left, up, stop]
};

function doSomething(i) {
    var fnmap = fnmaps[i] || [], j;
    for (j = 0; j < fnmap.length; j++) {
        fnmap[j]();
    }
}

doSomething(12);
doSomething(304);
doSomething(962);

通过编辑地图变量,可以简单地添加/排序函数。


12

你可以创建一个输入到操作的字典。在Java中,这将是一个Map<Integer, Runnable>,例如*:

map.put(12, () -> {
    up();
    left();
    stop();
});

然后,您可以获得相应的 Runnable 并运行它:

Runnable action = map.get(i);
if (action != null) {
    action.run();
} else {
    // some default action
    // This is the "default" case in a switch, or the "else" in an if-else
}

if-else并不是必须的,但是如果i不是预期值之一(即您放入映射中的值之一),那么您将会得到NullPointerException

在JavaScript中,思路类似,但使用对象代替Map、函数(可能是匿名的)代替Runnable等。


此代码适用于Java 8。在Java 7及以下版本中,您需要执行以下操作:

map.put(12, new Runnable() {
    @Override
    public void run() {
        up();
        left();
        stop();
    }
});


@Puru-- 这个想法在Java 7中同样适用。但是,是的,你需要创建一个匿名类来代替使用lambda表达式。 - yshavit
@sura 是的,如果您确信i始终是预期值,则不需要这样做。(如果您对此错误,将在action.run()处收到NullPointerException。) - yshavit
@yshavit,我想你可以使用try...catch来捕获NPE,而不是创建一个带有if...else的null-guard。 :) - Brian S
@BrianS 你可以这样做,但通常不是一个好主意。如果Runnable本身有错误并抛出NPE,由于那个catch-and-ignore,你永远不会知道。更好的方法是让它向上传递,这样某些东西就可以提醒你。 - yshavit
谁说你必须忽略异常?(并不是我认为try/catch是if/else的好替代品,仅仅是在这种情况下它可以,并且符合OP的要求。) - Brian S

4

哇哈哈哈哈,你救了我的一天 :) 这个应该可以用,现在没有测试的方法。

public void doSomething(int i) {

try {
    int x = 1/(12-i); // fails for i==12
} catch (ArithmeticException e) {
        up();
        left();
        stop();
}

以此类推,尽情享受吧!


4

如果没有条件检查,那么只能使用while和break来完成操作。

public void doSomething(int i) {

while(i == 12) {
// order should be same
    up();
    left();
    stop();
break;
    }

while(i == 304) {
// order should be same
    right();
    up();
    stop();
break;
    }

while(i == 962) {
// order should be same
    down();
    left();
    up();
    stop();
break;
    }
}

另一种同样糟糕的写法:(i == 12 && (up(), left(), stop())); (i == 304 && (right(), up(), stop())); (i == 962 && (down(), left(), up(), stop())); - gnasher729

2

对于JavaScript,您可以尝试以下方法:

// define ur actions here
var actions = {
  "12" : function () { up(); left(); stop(); },
  "304" : function () { right(); up(); stop(); },
  "962" : function () { down(); left(); up(); stop(); }
};

function doSomething(i) {
  var fn = actions[i];
  try {
    fn();
  } catch (err) {
    console.error(err);
  }
}
//
doSomething(12); //invoke here

非常聪明地编写。 - Ravichandra

1

看,自然而然地,你应该使用条件表达式来检查条件语句! 现在,如果你不想这样做,可以像这样 不自然地 进行:

先对所有方法(上、下、...)执行此操作

java.lang.reflect.Method method;
try {
  method = obj.getClass().getMethod(methodName, param1.class, param2.class, ..);
} catch (SecurityException e) {
  // ...
} catch (NoSuchMethodException e) {
  // ...
}

不要传递整数到doSomething中,而是使用包含要调用的方法名称的数组,并在for循环内部调用每个方法:

然后通过调用该方法来调用它:

try {
  method.invoke(obj, arg1, arg2,...);
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {

很遗憾,Java没有委托!

虽然我相信这种方法是可行的(即它将产生期望的结果),但在我看来,反射是解决这个问题不必要的复杂方案。 - Brian S
@BrianS- 确实,正如我之前所说,在Java中最自然的方法是使用if语句,但由于OP不想这样做,我尝试尽可能接近C#委托的方式来完成它,而Java中缺少了这个功能。 - Mohsen Kamrani
常见的Java解决方案是观察者模式的某个变体(yshavit的答案是简化版)。不过我要承认,几年前我写了一个类(http://clockworkgear.blogspot.com/2010/06/java-callbacks-using-javalangreflect.html)以可重用的方式实现您的解决方案。 - Brian S

1
这个问题可以使用面向对象编程技术来解决。
在Java中,它看起来像这样:
public abstract class AObject{
   public abstract  void doSomething();
   public void  up(){
      //do something here
   }
   public void down(){
      //do something here
   }
   public void left(){
      //do something here
   }
   public void right(){
      //do something here
   }       
   public void stop(){
      //do something here
   }     
}


public class AObject12 extends AObject{
   public void doSomething(){
      up();
      left();
      stop();
   }
}

public class AObject304 extends AObject{
   public void doSomething(){
      right();
      up();
      stop();
   }
}

public class AObject962 extends AObject{
   public void doSomething(){
      down();
      left();
      up();
      stop();
   }
}

在具体类的实例上执行 doSomething 将触发相应的行为。因此,不再需要 if/else。请参见下面的代码示例:
AObject A12 = new AObject12();
A12.doSomething();
// Will run Up left stop in that order

AObject A304 = new AObject304();
A304.doSomething();
// Will run right Up stop in that order

AObject A962 = new AObject962();
A962.doSomething();
// Will run down left up stop in that order

关于这种编程的更多信息可以在这里找到:http://en.wikipedia.org/wiki/Polymorphism_%28computer_science%29

如果您希望能够动态地改变对象的行为,可以考虑应用状态模式:http://en.wikipedia.org/wiki/State_pattern


1
你现在如何有条件地调用一个条件? - Ven
@Ven 你可以在一个对象的实例上调用do instance方法,它将执行属于该实例的代码。假设你希望这种行为是可设置的,那么你可以考虑使用状态模式:http://en.wikipedia.org/wiki/State_pattern。 - Eric
但是你如何实例化正确的类?你没有回答主要问题。 - Ven
那仍然是不正确的,你正在执行每个行为,而不是根据“i”的值执行一个。 - Ven
i的值在对象中表示,它不再是一个变量。但它取决于对象的类型。它仍然包含与OP代码类似的逻辑,但没有if/else,就像OP所要求的那样。 :) - Eric
你忽略了我之前的评论。你在你的最后代码中执行了每个行为。 - Ven

0

可以通过以下方式在JAVA中使用逻辑AND运算符的短路来实现所需的行为:

public static Boolean seq12() {
    // order should be same
    up();
    left();
    stop();
    return true;
}
public static Boolean seq304() {
    // order should be same
    right();
    up();
    stop();
    return true;
}
public static Boolean seq962() {
    // order should be same
    down();
    left();
    up();
    stop();
    return true;
}

public static void doSomething(int i) {
    Boolean tmp;
    tmp = (i == 12 && seq12());
    tmp = (i == 304 && seq304());
    tmp = (i == 962 && seq962());
}

这段代码非常直观,它依赖于逻辑 AND 运算符只有在第一个操作数为 true 时才会评估第二个操作数的事实。


0

在Java中,这应该可以工作。

public class IfTest {

    public static void main(String[] args) {
        IfTest ifTest = new IfTest();

        ifTest.doSomething(12);
        ifTest.doSomeThingWithoutIf(12);

        ifTest.doSomething(304);
        ifTest.doSomeThingWithoutIf(304);

        ifTest.doSomething(962);
        ifTest.doSomeThingWithoutIf(962);

        ifTest.doSomething(42);
        ifTest.doSomeThingWithoutIf(42);
    }

    public void doSomeThingWithoutIf(int i) {


        boolean x = i == 12 ? f12() : (i == 304 ? f304() : (i == 962 ? f962() : false));
    }

    public boolean f12() {

        up();
        left();
        stop();

        return true;
    }

    public boolean f304() {

        right();
        up();
        stop();
        return true;
    }

    public boolean f962() {

        down();
        left();
        up();
        stop();

        return true;
    }

    public void doSomething(int i) {

        if(i == 12) {
            // order should be same
            up();
            left();
            stop();
        }

        if(i == 304) {
            // order should be same
            right();
            up();
            stop();
        }

        if(i == 962) {
            // order should be same
            down();
            left();
            up();
            stop();
        }
    }

    private boolean retfalse() {
        return false;
    }

    private void down() {
        System.out.println("down");
    }

    private void right() {
        System.out.println("right");
    }

    private void stop() {
        System.out.println("stop");
    }

    private void left() {
        System.out.println("left");
    }

    private void up() {
        System.out.println("up");
    }
}

我猜是因为你使用了三元运算符的if-else语句。我认为OP想要一个没有if-else或其同义词的解决方案。 - TheOneWhoPrograms
如果是这种情况,他一定必须说明“不使用条件运算符”之类的。我对JS不太熟悉,但是如果我看一下Java发布的解决方案,它们要么使用反射,要么使用Map / Dictionary等等。我认为这两种方法在性能和内存占用方面都很糟糕,而这只是一件简单的事情。 - senseiwu
1
我并不完全不同意你的观点,zencv。我只是提供了一个可能导致它被踩的原因 ;) - TheOneWhoPrograms

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接