【问题标题】:Solution for Magic Number issue......?幻数问题的解决方案......?
【发布时间】:2013-03-21 04:21:30
【问题描述】:

考虑以下代码段...

 public static UserStatus getEnum(int code) {
    switch (code) {
        case 0:
            return PENDING;
        case 1:
            return ACTIVE;
        case 2:
            return SUSPENDED;
        case 3:
            return DELETED;
        case 4:
            return LOGIN_DISABLED;
        default:
            return null;
        }

}

现在,SONAR 将案例(案例 3 和案例 4)中的 3 号和 4 号检测为幻数。

为避免该问题,我将代码段更改如下...

 public static UserStatus getEnum(int code) {        
    final int Pending=0;
    final int Active=1;
    final int Suspended=2;
    final int Deleted= 3;
    final int Login_details=4;

    switch (code) {
        case Pending:
            return PENDING;
        case Active:
            return ACTIVE;
        case Suspended:
            return SUSPENDED;
        case Deleted:
            return DELETED;
        case Login_details:
            return LOGIN_DISABLED;
        default:
            return null;
    }
}

在这种情况下,这是解决幻数问题的好方法吗?

【问题讨论】:

  • 3 和 4 被“检测为幻数”是什么意思?什么是“幻数问题”?
  • 我假设您在谈论 SONAR 将它们检测为 Magic Numbers
  • 是的,你是对的:查看其他问题以供参考:stackoverflow.com/questions/47882/…
  • 更好的选择是拥有一个具有static 值的类,以便可以将经常使用的Magic 数字移到那里,这样它们就可以在项目中的任何地方使用。
  • 这是关于声纳的。 SONAR 将这些检测为幻数

标签: java switch-statement sonarqube magic-numbers


【解决方案1】:

我了解到您希望避免在代码中使用整数文字。您的解决方案不是特别有效,因为它只是将文字移动到方法的顶部。它获得了一点好处,因为它为常量提供了有意义的名称,但这些名称是方法私有的。

更好的方法是将数字定义为接口中的字段。然后,您可以静态导入字段并将它们用作常量的符号名称。

如果枚举的声明顺序与常量相同:

enum UserStatus {PENDING, ACTIVE, SUSPENDED, DELETED, LOGIN_DISABLED}

你可以做另一个技巧:

public static UserStatus getEnum(int code) {
    UserStatus[] values = UserStatus.values();
    return (code >= 0 && code < values.length) ? values[code] : null;
}

但是,这会在常量值和枚举声明之间建立联系。这可能没问题,具体取决于在对getEnum 的调用中生成实际参数值的位置。

【讨论】:

  • 接口常量在 Java 中是反模式。
  • @jFrenetic - 是的,现在使用类来声明符号常量被认为是更好的风格。无论哪种方式都将是对 OP 当前代码的改进。
  • 我认为这与将常量放在“类”或“接口”中无关。反模式是:创建一个仅用于放置常量的接口。常量应该转到适合包含它的类/接口/类型。如果没有,则使用最终的不可实例化类,以避免给封闭类型提供不正确的语义含义
  • @AdrianShum - 真正的反模式是通过实现接口将常量带入类的命名空间。出于这个原因,“类”与。 “界面”是一个重要的区别。 (您扩展而不是实现类。)使用import static 并在(不可实例化的)类中而不是接口中声明常量会将其排除在反模式的范围之外。 (例如,参见维基百科文章Constant interface。)
  • @jFrenetic - 反模式是实现一个常量接口,而不是声明一个并静态导入常量名称。
【解决方案2】:

问题是直截了当且显而易见的:当人们阅读您的代码时,为什么 1 会给出 PENDING 并不明显。 1是什么意思?

你应该赋予它语义意义。使用常量是通常应该做的:

(假设getEnum() 是UserService 的一部分,代码应该类似于)

public interface UserService {
   public static final int USER_STATUS_VAL_PENDING = 1;
   public static final int USER_STATUS_VAL_ACTIVE = 2;

   UserStatus getUserStatus(int userStatusVal);
}

public class SimpleUserService implements UserService {
   public UserStatus getUserStatus(int userStatusVal) {
      switch userStatusVal {
      case USER_STATUS_VAL_PENDING:
         return UserStatus.PENDING;
      case USER_STATUS_VAL_ACTIVE:
         return UserStatus.ACTIVE;
      //.....
   }
}

正如另一个答案所建议的,您可以依靠 enum 的序数值来进行 int-enum 映射。但是您必须注意,如果您重新排列枚举的值,或者在远离末尾的位置添加新值,这种方式可能会导致问题。序数值将被更改,您无法覆盖它。

另外需要注意的是,你的代码中有一些事情没有做对:

  1. 由于目的是通过将整数文字设为常量来赋予其语义意义,因此它应该对调用者可见,因此局部 final 变量不是正确的做法。
  2. 您应该使用 ALL_CAP_UNDERSCORE_DELIMITED 作为常量名称

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-07
    • 2021-08-19
    • 2013-05-18
    • 1970-01-01
    相关资源
    最近更新 更多