如何以更优雅和可扩展的方式编写这些条件语句

时间:2019-02-16 13:08:36

标签: java algorithm if-statement

在我的软件中,我需要根据2个参数确定功能的版本。例如。

Render version 1 -> if (param1 && param2) == true;
Render version 2 -> if (!param1 && !param2) == true;
Render version 3 -> if only param1 == true;
Render version 4 -> if only param2 == true;

因此,为了满足此要求,我编写了一个看起来像这样的代码-

if(param1 && param2) //both are true {
    version = 1;
}
else if(!param1 && !param2) //both are false {
    version = 2;
}
else if(!param2) //Means param1 is true {
    version = 3;
}
else { //Means param2 is true
    version = 4;
}

肯定有多种编码方法,但是我尝试了不同的方法后最终确定了此方法,因为这是我能想到的最易读的代码。
但是这段代码绝对不可扩展,因为-

  1. 让我们说明天我们要引入一个称为param3的新参数。然后 没有支票将增加,因为可能 组合。
  2. 对于此软件,我非常确定我们 将来将不得不容纳新的参数。

可以使用任何可扩展且可读的方式来编码这些要求吗?

5 个答案:

答案 0 :(得分:5)

如果必须枚举所有可能的布尔值组合,将它们转换成数字通常是最简单的:

//                            param1:   F  T  F  T
//                            param2;   F  F  T  T
static final int[] VERSIONS = new int[]{2, 3, 4, 1};

...
version = VERSIONS[(param1 ? 1:0) + (param2 ? 2:0)];

答案 1 :(得分:5)

编辑: 对于可伸缩解决方案,请通过Map为每个参数组合定义版本:

Map<List<Boolean>, Integer> paramsToVersion = Map.of(
        List.of(true, true), 1,
        List.of(false, false), 2,
        List.of(true, false), 3,
        List.of(false, true), 4);

现在找到合适的版本是一个简单的地图查找:

    version = paramsToVersion.get(List.of(param1, param2));

从Java 9开始,我初始化地图的方式就起作用了。在Java的旧版本中,它有点罗word,但可能仍然值得一做。即使在Java 9中,如果您有4个或更多参数(对于16个组合),也需要使用Map.ofEntries,这也比较罗word。

原始答案: 我的口味是嵌套if / else语句,并且只测试一次每个参数:

    if (param1) {
        if (param2) {
            version = 1;
        } else {
            version = 3;
        }
    } else {
        if (param2) {
            version = 4;
        } else {
            version = 2;
        }
    }

但是它无法很好地扩展到许多参数。

答案 2 :(得分:4)

我怀疑是否有一种方法可以同时变得更紧凑,可读性和可伸缩性。

您将条件表示为最小化表达式,该表达式紧凑且可能有意义(特别是无关的变量不会使它们混乱)。但是没有可以利用的系统主义。

一个非常系统的替代方案可以是真值表,即所有组合的显式扩展以及相关的真值(或版本号),这在运行时间方面可能非常有效。但是这些变量的大小呈指数形式,而且可读性不强。

恐怕没有免费的午餐。您当前的解决方案非常好。


如果您追求效率(即避免需要顺序求值所有表达式),则可以考虑采用真值表方法,但是可以采用以下方式:

  • 声明一个包含2 ^ n个条目的版本号数组;

  • 使用代码就像编写一样初始化所有表条目;为此,请枚举[0,2 ^ n)中的所有整数并使用其二进制表示形式;

  • 现在进行查询,从n个输入布尔值中形成一个整数索引并查找数组。

使用Olevv的答案,表格将是[2,4,3,1]。查找将类似于(false,true)=> T [01b] = 4。

重要的是,原始表达式集仍然存在于代码中,以供人类阅读。您可以在运行时将其填充到数组的初始化函数中使用它,也可以使用它对表进行硬编码(并将代码保留在注释中;甚至更好的是,将生成硬编码的代码保留在表中)。表格)。

答案 3 :(得分:4)

您的参数组合不过是一个二进制数(如01100),其中0表示false,而1表示true

因此,可以通过使用1和0的所有组合轻松计算您的版本。可能有2个输入参数的组合是:

  1. 11->都是true
  2. 10->第一个是true,第二个是false
  3. 01->第一个是false,第二个是true
  4. 00->都是false

因此,借助这些知识,我提出了一个使用“位掩码”(不超过数字)和“位操作”的可扩展性很强的解决方案:

public static int getVersion(boolean... params) {
    int length = params.length;
    int mask = (1 << length) - 1;
    for(int i = 0; i < length; i++) {
        if(!params[i]) {
            mask &= ~(1 << length - i - 1);
        }
    }
    return mask + 1;
}

最有趣的一行可能是这样:

mask &= ~(1 << length - i - 1);

它一次完成很多事情,我将其拆分。 length - i - 1部分从右边开始计算“位”在位掩码中的位置(0,例如在数组中)。

下一部分:1 << (length - i - 1)将数字1左移数量。假设我们的位置为3,则操作1 << 22是第三个位置)的结果将是值100的二进制数。

~符号是一个二进制逆,因此所有位都被反转,所有0都变为1,所有1都变为{{1} }。在前面的示例中,0的倒数为100

最后一部分:011mask &= n相同,其中mask = mask & n是先前计算的值n。这只不过是二进制AND,因此保留011mask中所有相同的位,并丢弃所有其他位。

总而言之,如果输入参数为n,那么此行只不过是删除mask给定位置的“位”。


如果版本号不是从false1连续的,那么像this one这样的版本查找表可能会对您有所帮助。

整个代码只需要在最后一行进行一次调整:

4

您的return VERSIONS[mask]; 数组按顺序包含所有版本,但相反。 (VERSIONS的索引0是两个参数均为假的位置)

答案 4 :(得分:2)

我本来应该和:

if (param1) {
    if (param2) {
    } else {
    }
} else {
    if (param2) {
    } else {
    }
}

种类重复,但每个条件仅评估一次,您可以轻松找到针对任何特定组合执行的代码。当然,添加第三个参数会使代码加倍。但是,如果有任何无效的组合,则可以省去那些会缩短代码的组合。或者,如果您想为它们抛出异常,则很容易查看您错过了哪种组合。如果IF太长,您可以在方法中显示实际代码:

if (param1) {
    if (param2) {
        method_12();
    } else {
        method_1();
    }
} else {
    if (param2) {
        method_2();
    } else {
        method_none();
    }
}

因此,您的整个开关逻辑将承担自身的功能,而任何组合的实际代码都在另一种方法中。当需要使用特定组合的代码时,只需查找适当的方法即可。大型IF迷宫很少被看到,当它出现时,它仅包含IF本身,而没有其他可能使人分心的东西。