公式中的幻数

时间:2014-07-15 06:01:38

标签: java

这是一个完善的惯例,应避免使用魔法数字。但复杂的条件公式中的幻数呢?例如:

int result = 0;
if (level <= 50) {
    result = (int) (Math.pow(level, 3) * (100 - level) / 50);
}
else if (level <= 68 && level > 50) {
    result = (int) (Math.pow(level, 3) * (150 - level) / 100);
}
else if (level <= 98 && level > 68) {
    result = (int) (Math.pow(level, 3) * ((1911 - 10 * level) / 3) / 500);
}
else if (level < 100 && level > 98) {
    result = (int) (Math.pow(level, 3) * (160 - level) / 100);
}
return result;

在这种情况下,最好只说'#34;尽可能避免使用魔法数字&#34;?我也在eclipse中使用CheckStyle来帮助向我显示我可能错过了一个真正的幻数。但是,没有办法禁用某些数字的检查而不是其他数字。

7 个答案:

答案 0 :(得分:5)

我认为所有魔法数字都应该声明为常量或正确记录。

首选声明文件。

否则,开发人员不清楚这些数字是什么以及它们为什么存在。

答案 1 :(得分:2)

这不仅需要用命名常量替换幻数,我建议使用一些命名的中间变量。例如,((1911 - 10 * level) / 3) / 500)的含义到底是什么?应将其分配给具有有意义名称的变量。这是社会保障金和年龄吗?

OP不理解数字或等式的事实回答了这个问题。他将如何支持此代码?

答案 2 :(得分:0)

我会先尝试简化代码。

int l3 = level * level * level;

return level <= 50 ? l3 * (100 - level) / 50 :
        level <= 68 ? l3 * (150 - level) / 100 :
                level <= 98 ? l3 * ((1911 - 10 * level) / 3) / 500 :
                        level < 100 ? l3 * (160 - level) / 100 : 0;

然后我会看看为什么如果等级> = 100,结果为0。

由于这看起来是随意的,因此你可能很难解释这些神奇的数字,因为你可能已经制作了它们,但同样你可能会发现你可以简化公式,使其更容易计算和解释,但仍然具有所需的形状。 / p>

答案 3 :(得分:0)

这有点像风格和品味。一般建议是:当从上下文中明显看出你的意思和原因时,或者当它们的使用本身就是它们的定义时 - 也就是说,当数字的含义和原因没有其他解释时,请使用幻数。例如,如果我定义一个函数:

0 if x < 28 else 1

并没有更具体的说明这个数字而不是“我用来定义我的函数的阈值”并且在你的程序中没有其他常量等于28并且永远不会,那么你可能会把它保留为是

在您的情况下,您可能通过此公式定义这些阈值,并且没有任何关于它们的说法,那么您可以将它们保留原样。

如果您可以为该号码指定一个名称和/或评论该号码具有此值的原因,那么使用名称总是更好:

var age  = 52; // my age
var code = 52; // my country's long distance code

即使你对一个数字没什么可说的,最好给它一个名字,因为如果你有两个这样的数字,你以后就不会知道它们是否是独立的。即使现在你没有平等,有一天你可能会。在上面的示例中,明年我将转为53并且必须修改公式:

a = 52 * x + y + z + 52;

但是这两个中哪一个是我的年龄,哪个是代码?一年前,当我写这个程序时,它是

a = 51 * x + y + z + 52;

对我来说很明显哪个是我的年龄,哪个是我的代码,但是在我52岁之后并且相应地修改了程序!

答案 4 :(得分:0)

你可以尝试类似下面的东西:(它没有精炼,尝试提出一个单一的公式/方程式,并根据检查替换变量中的值)

int d = 1;
    int x = 3;
    int l = 1;
    int f = 1;
    int y = 0;

    if (level <= 50) {
        y = 100;
        d = 50;
    } else if (level <= 68 && level > 50) {
        y = 150;
        d = 100;
    } else if (level <= 98 && level > 68) {
        y = 1911;
        l = 10;
        f = 500;
        d=3;
    } else if (level < 100 && level > 98) {
        y = 160;
        d = 100;
    }
    return (int) (Math.pow(level, x) * ((y - level * l) / d) / f);

答案 5 :(得分:0)

正如拉斐尔所说,魔术数字应该被声明为常数并且有适当的记录,

再添加一件东西来改进它,你正在三次执行相同的操作(我不确定你在第3个条件下做了什么,如果条件相同,你可以考虑四次)所以你可以声明一个提高具有许多幻数的代码的可读性的方法如下,

public int calcResult(int arg1, int arg2, int arg3)
{
   return (int)(Math.pow(level, arg1) * (arg2 - level) / arg3);
}

.... 

int result = 0;
if (level <= 50) {
    result = calcResult(3,100,50); // these can be replaced with constants.
}
else if (level <= 68 && level > 50) {
    result = calcResult(3, 150, 100);
}
else if (level <= 98 && level > 68) {
    result = (int) (Math.pow(level, 3) * ((1911 - 10 * level) / 3) / 500); // You can also change this accordingly.
}
else if (level < 100 && level > 98) {
    result = calcResult(3, 160, 100);
}
return result;

以上代码对用户来说更具可读性,因为方法正在执行相同的操作。用常数替换幻数将会进一步改善它。

答案 6 :(得分:0)

编程不是具有硬性规则的东西。编程就是要做出权衡。为了实现良好的权衡,我们有一些商定的良好做法。对魔术数字皱眉是其中之一。

这并不意味着应该始终避免使用魔法数字。以下示例是避免幻数练习的愚蠢用法:

final int ONE = 1; // as if the value of 1 is going to change in the future

但这是可以接受的:

final int BITS_TO_SHIFT = 1;
// and further down in the code
int shifted_value = value >> BITS_TO_SHIFT;

话虽如此,让我们看看你提供的代码 是否需要幻数:

  1. 我们可以将立方体幻数移动到常数:

    final int CUTOFF_1 =  50;
    final int CUTOFF_2 =  68;
    final int CUTOFF_3 =  98;
    final int MAX      = 100;
    
    
    int result = 0;
    if (level <= CUTOFF_1) {
        result = (int) (Math.pow(level, 3) * (100 - level) / 50);
    }
    else if (level <= CUTOFF_2 && level > CUTOFF_1) {
        result = (int) (Math.pow(level, 3) * (150 - level) / 100);
    }
    else if (level <= CUTOFF_3 && level > CUTOFF_2) {
        result = (int) (Math.pow(level, 3) * ((1911 - 10 * level) / 3) / 500);
    }
    else if (level < MAX && level > CUTOFF_3) {
        result = (int) (Math.pow(level, 3) * (160 - level) / 100);
    }
    return result;
    
  2. 我认为看起来更好。如果发生截止数的变化,则只需要更新常数。我相信大多数其他常量也可以通过这种方式删除。但不知道他们是什么我什么也做不了。

  3. 请注意,作为一个陌生人,我将这些神奇的数字视为任意数字的集合。创建具有描述常量目的的适当名称的常量不是更好吗?